@sjolystinnovation/app-kit 0.15.0 → 0.16.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
@@ -170,7 +170,7 @@ export { handler as GET, handler as POST };
170
170
 
171
171
  | Setting | Type | Description |
172
172
  | --- | --- | --- |
173
- | `apiUrl` | `string` | Base URL of the API. Sign-in posts to `/auth/login` and refresh to `/auth/refresh-token` under it. An empty value throws. |
173
+ | `apiUrl` | `string` | Base URL of the API. Sign-in posts to `/auth/login`, refresh to `/auth/refresh-token` and sign-out to `/auth/logout` under it. An empty value throws. |
174
174
  | `env` | `Record<string, string \| undefined>` | The server environment, usually `process.env`. It supplies the secret named by `jwtSecretEnvVar`. |
175
175
  | `http` | `AxiosInstance` | In place of `apiUrl`, for tests. The client for sign-in and refresh, with its own base URL. |
176
176
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjolystinnovation/app-kit",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "Building blocks for Next.js apps that sign in against a JWT API through next-auth",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Sjølyst Innovation AS",
@@ -73,6 +73,10 @@ const REMEMBER_ROTATION_MS = 60 * 1000;
73
73
  // Sign-in and session reads wait for the API, so a hung connection must not hold them forever.
74
74
  const API_TIMEOUT_MS = 10_000;
75
75
 
76
+ // Sign-out waits for the API to drop the refresh token, but only briefly, because the cookie is
77
+ // cleared either way.
78
+ const SIGN_OUT_TIMEOUT_MS = 3_000;
79
+
76
80
  function rolesOf(claims: ApiClaims): string[] {
77
81
  const raw = claims.role;
78
82
  return Array.isArray(raw) ? raw : raw ? [raw] : [];
@@ -249,6 +253,19 @@ export function createAuthOptions(config: SessionConfig, settings: AuthOptionsSe
249
253
  session: {
250
254
  maxAge: ABSOLUTE_SESSION_MAX_AGE / 1000,
251
255
  },
256
+ events: {
257
+ // Sign-out also asks the API to delete the refresh token, so a copy of it cannot be used again.
258
+ // A failure leaves sign-out to the cookie alone.
259
+ async signOut({ token }) {
260
+ const refreshToken = (token as AppToken | null)?.refreshToken;
261
+ if (!refreshToken) return;
262
+ try {
263
+ await http.post("/auth/logout", { refreshToken }, { timeout: SIGN_OUT_TIMEOUT_MS });
264
+ } catch (error) {
265
+ console.error("Sign-out could not delete the refresh token at the API", failureShape(error));
266
+ }
267
+ },
268
+ },
252
269
  callbacks: {
253
270
  async jwt({ token: current, user }) {
254
271
  let token = current as AppToken;