Repository: markusahlstrand/authhero
Pull request #57: handle idle expires in silent auth
Author: login: markusahlstrand
Created: 2025-03-13T02:12:53Z | Merged: 2025-03-13T02:24:10Z

Silent auth was expecting a expires at, but now we have both expires_at and idle_expires_at and they are both optional.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **Chores**
  - Released a new patch with updated version numbers and dependency upgrades.

- **Authentication Improvements**
  - Enhanced silent login flow with improved session validation and error reporting for a smoother user experience.

- **Database Enhancements**
  - Increased the capacity for authorization URLs to ensure more robust data handling.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/apps/demo/CHANGELOG.md b/apps/demo/CHANGELOG.md
index c334ab4ed..76f33b1d9 100644
--- a/apps/demo/CHANGELOG.md
+++ b/apps/demo/CHANGELOG.md
@@ -1,5 +1,13 @@
 # @authhero/demo
 
+## 0.9.28
+
+### Patch Changes
+
+- Updated dependencies
+  - authhero@0.86.0
+  - @authhero/kysely-adapter@9.4.0
+
 ## 0.9.27
 
 ### Patch Changes
diff --git a/apps/demo/package.json b/apps/demo/package.json
index 1126077a1..60bc683f1 100644
--- a/apps/demo/package.json
+++ b/apps/demo/package.json
@@ -1,16 +1,16 @@
 {
   "name": "@authhero/demo",
   "private": true,
-  "version": "0.9.27",
+  "version": "0.9.28",
   "scripts": {
     "dev": "bun --watch src/bun.ts"
   },
   "dependencies": {
-    "@authhero/kysely-adapter": "^9.3.0",
+    "@authhero/kysely-adapter": "^9.4.0",
     "@hono/swagger-ui": "^0.5.0",
     "@hono/zod-openapi": "^0.18.3",
     "@peculiar/x509": "^1.12.3",
-    "authhero": "^0.85.0",
+    "authhero": "^0.86.0",
     "hono": "^4.6.15",
     "hono-openapi-middlewares": "^1.0.11",
     "kysely": "^0.27.4",
diff --git a/packages/authhero/CHANGELOG.md b/packages/authhero/CHANGELOG.md
index 560fbae08..89b7e5b71 100644
--- a/packages/authhero/CHANGELOG.md
+++ b/packages/authhero/CHANGELOG.md
@@ -1,5 +1,11 @@
 # authhero
 
+## 0.86.0
+
+### Minor Changes
+
+- Hande idle_expires_at in silent auth
+
 ## 0.85.0
 
 ### Minor Changes
diff --git a/packages/authhero/package.json b/packages/authhero/package.json
index 2e69b0053..eb41b397e 100644
--- a/packages/authhero/package.json
+++ b/packages/authhero/package.json
@@ -1,6 +1,6 @@
 {
   "name": "authhero",
-  "version": "0.85.0",
+  "version": "0.86.0",
   "files": [
     "dist"
   ],
diff --git a/packages/authhero/src/authentication-flows/silent.ts b/packages/authhero/src/authentication-flows/silent.ts
index aced6cbf8..b80204675 100644
--- a/packages/authhero/src/authentication-flows/silent.ts
+++ b/packages/authhero/src/authentication-flows/silent.ts
@@ -41,78 +41,100 @@ export async function silentAuth({
   response_type,
 }: SilentAuthParams) {
   const { env } = ctx;
-
   const redirectURL = new URL(redirect_uri);
+  const originUrl = `${redirectURL.protocol}//${redirectURL.host}`;
 
-  if (session?.expires_at && new Date(session.expires_at) > new Date()) {
-    ctx.set("user_id", session.user_id);
+  // Helper function to handle login required scenarios
+  async function handleLoginRequired(description: string = "Login required") {
+    const log = createLogMessage(ctx, {
+      type: LogTypes.FAILED_SILENT_AUTH,
+      description,
+    });
+    await ctx.env.data.logs.create(client.tenant.id, log);
 
-    // Update the cookie
-    const headers = new Headers();
-    const cookie = serializeAuthCookie(client.tenant.id, session.id);
-    headers.set("set-cookie", cookie);
+    return ctx.html(
+      renderAuthIframe(
+        originUrl,
+        JSON.stringify({
+          error: "login_required",
+          error_description: description,
+          state,
+        }),
+      ),
+    );
+  }
 
-    const user = await env.data.users.get(client.tenant.id, session.user_id);
+  // Check if session is valid
+  const isSessionExpired =
+    !session ||
+    (session?.expires_at && new Date(session.expires_at) < new Date()) ||
+    (session?.idle_expires_at &&
+      new Date(session.idle_expires_at) < new Date());
 
-    if (user) {
-      ctx.set("username", user.email);
-      ctx.set("connection", user.connection);
+  if (isSessionExpired) {
+    return handleLoginRequired();
+  }
 
-      const tokenResponse = await createAuthTokens(ctx, {
-        client,
-        authParams: {
-          client_id: client.id,
-          audience,
-          code_challenge_method,
-          code_challenge,
-          scope,
-          state,
-          nonce,
-          response_type,
-        },
-        user,
-        session_id: session.id,
-      });
-
-      await env.data.sessions.update(client.tenant.id, session.id, {
-        used_at: new Date().toISOString(),
-        expires_at: new Date(
-          Date.now() + SILENT_AUTH_MAX_AGE_IN_SECONDS * 1000,
-        ).toISOString(),
-      });
-
-      const log = createLogMessage(ctx, {
-        type: LogTypes.SUCCESS_SILENT_AUTH,
-        description: "Successful silent authentication",
-      });
-      await ctx.env.data.logs.create(client.tenant.id, log);
-
-      return ctx.html(
-        renderAuthIframe(
-          `${redirectURL.protocol}//${redirectURL.host}`,
-          JSON.stringify(tokenResponse),
-        ),
-        {
-          headers,
-        },
-      );
-    }
+  ctx.set("user_id", session.user_id);
+
+  const user = await env.data.users.get(client.tenant.id, session.user_id);
+
+  if (!user) {
+    return handleLoginRequired("User not found");
   }
 
+  ctx.set("username", user.email);
+  ctx.set("connection", user.connection);
+
+  // Create authentication tokens
+  const tokenResponse = await createAuthTokens(ctx, {
+    client,
+    authParams: {
+      client_id: client.id,
+      audience,
+      code_challenge_method,
+      code_challenge,
+      scope,
+      state,
+      nonce,
+      response_type,
+    },
+    user,
+    session_id: session.id,
+  });
+
+  // Update session
+  await env.data.sessions.update(client.tenant.id, session.id, {
+    used_at: new Date().toISOString(),
+    last_interaction_at: new Date().toISOString(),
+    device: {
+      ...session.device,
+      last_ip: ctx.req.header("x-real-ip") || "",
+      last_user_agent: ctx.req.header("user-agent") || "",
+    },
+    idle_expires_at: session.idle_expires_at
+      ? new Date(
+          Date.now() + SILENT_AUTH_MAX_AGE_IN_SECONDS * 1000,
+        ).toISOString()
+      : undefined,
+  });
+
+  // Log successful authentication
   const log = createLogMessage(ctx, {
-    type: LogTypes.FAILED_SILENT_AUTH,
-    description: "Login required",
+    type: LogTypes.SUCCESS_SILENT_AUTH,
+    description: "Successful silent authentication",
   });
   await ctx.env.data.logs.create(client.tenant.id, log);
 
-  return ctx.html(
-    renderAuthIframe(
-      `${redirectURL.protocol}//${redirectURL.host}`,
-      JSON.stringify({
-        error: "login_required",
-        error_description: "Login required",
-        state,
-      }),
-    ),
-  );
+  // Set response headers
+  const headers = new Headers();
+  // The following header is added to prevent Cloudflare from adding the beacon script to the file which might mess with Safari ITP
+  headers.set("Server-Timing", "cf-nel=0; no-cloudflare-insights=1");
+
+  const cookie = serializeAuthCookie(client.tenant.id, session.id);
+  headers.set("set-cookie", cookie);
+
+  return ctx.html(renderAuthIframe(originUrl, JSON.stringify(tokenResponse)), {
+    headers,
+  });
 }
diff --git a/packages/authhero/test/routes/auth-api/authorize.spec.ts b/packages/authhero/test/routes/auth-api/authorize.spec.ts
index 037765151..a80eeb50b 100644
--- a/packages/authhero/test/routes/auth-api/authorize.spec.ts
+++ b/packages/authhero/test/routes/auth-api/authorize.spec.ts
@@ -138,13 +138,13 @@ describe("authorize", () => {
       const { oauthApp, env } = await getTestServer();
       const oauthClient = testClient(oauthApp, env);
 
-      const expires_at = new Date(Date.now() + 1000).toISOString();
+      const idle_expires_at = new Date(Date.now() + 1000).toISOString();
 
       await env.data.sessions.create("tenantId", {
         id: "sessionId",
         user_id: "email|userId",
         clients: ["clientId"],
-        expires_at,
+        idle_expires_at,
         device: {
           last_ip: "",
           initial_ip: "",
@@ -185,7 +185,7 @@ describe("authorize", () => {
       // Fetch the session
       const session = await env.data.sessions.get("tenantId", "sessionId");
       expect(session?.used_at).toBeDefined();
-      expect(session?.expires_at).not.toBe(expires_at);
+      expect(session?.idle_expires_at).not.toBe(idle_expires_at);
     });
 
     it("should return a web_message response with login required for a expired session", async () => {
diff --git a/packages/kysely/CHANGELOG.md b/packages/kysely/CHANGELOG.md
index 584b0d784..56989177f 100644
--- a/packages/kysely/CHANGELOG.md
+++ b/packages/kysely/CHANGELOG.md
@@ -1,5 +1,11 @@
 # @authhero/kysely-adapter
 
+## 9.4.0
+
+### Minor Changes
+
+- Hande idle_expires_at in silent auth
+
 ## 0.10.0
 
 ### Minor Changes
diff --git a/packages/kysely/migrate/migrations/2025-03-12T14:14:00_authorization_url_length.ts b/packages/kysely/migrate/migrations/2025-03-12T14:14:00_authorization_url_length.ts
new file mode 100644
index 000000000..5581013c9
--- /dev/null
+++ b/packages/kysely/migrate/migrations/2025-03-12T14:14:00_authorization_url_length.ts
@@ -0,0 +1,32 @@
+import { Kysely } from "kysely";
+import { Database } from "../../src/db";
+
+/**
+ * Up migration: Add a new custom domains table
+ */
+export async function up(db: Kysely<Database>): Promise<void> {
+  await db.schema
+    .alterTable("logins")
+    .dropColumn("authorization_url")
+    .execute();
+
+  await db.schema
+    .alterTable("logins")
+    .addColumn("authorization_url", "varchar(2048)")
+    .execute();
+}
+
+/**
+ * Down migration: restore the domains table
+ */
+export async function down(db: Kysely<Database>): Promise<void> {
+  await db.schema
+    .alterTable("logins")
+    .dropColumn("authorization_url")
+    .execute();
+
+  await db.schema
+    .alterTable("logins")
+    .addColumn("authorization_url", "varchar(1024)")
+    .execute();
+}
diff --git a/packages/kysely/migrate/migrations/index.ts b/packages/kysely/migrate/migrations/index.ts
index 0b8825b5b..3404dd935 100644
--- a/packages/kysely/migrate/migrations/index.ts
+++ b/packages/kysely/migrate/migrations/index.ts
@@ -72,6 +72,7 @@ import * as n72_session_primary_key from "./2025-02-12T13:15:00_session_primary_
 import * as n73_drop_sessions from "./2025-02-12T15:27:00_drop_sessions";
 import * as n74_custom_domains from "./2025-02-21T23:45:00_custom_domains";
 import * as n75_organizations from "./2025-03-10T11:20:00_organization";
+import * as n76_authorization_url_length from "./2025-03-12T14:14:00_authorization_url_length";
 
 // These need to be in alphabetic order
 export default {
@@ -149,4 +150,5 @@ export default {
   n73_drop_sessions,
   n74_custom_domains,
   n75_organizations,
+  n76_authorization_url_length,
 };
diff --git a/packages/kysely/package.json b/packages/kysely/package.json
index 22f76d6e1..946cde028 100644
--- a/packages/kysely/package.json
+++ b/packages/kysely/package.json
@@ -11,7 +11,7 @@
     "type": "git",
     "url": "https://github.com/markusahlstrand/authhero"
   },
-  "version": "9.3.0",
+  "version": "9.4.0",
   "files": [
     "dist"
   ],


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🧹 Nitpick comments (8)</summary><blockquote>

<details>
<summary>packages/authhero/package.json (1)</summary><blockquote>

`3-3`: **Version Bump Confirmed.**  
The package version has been updated to "0.86.0", which is consistent with the intended enhancements (supporting idle expiration handling in silent auth). Please verify that all related documentation (e.g. README, CHANGELOG) and dependent packages update accordingly.

</blockquote></details>
<details>
<summary>packages/kysely/CHANGELOG.md (1)</summary><blockquote>

`7-8`: **Typographical Correction Needed.**  
The changelog entry under version 9.4.0 currently reads “Hande idle_expires_at in silent auth.” Please correct “Hande” to “Handle” for clarity and professionalism.

</blockquote></details>
<details>
<summary>packages/authhero/CHANGELOG.md (1)</summary><blockquote>

`7-7`: **Changelog Text Correction.**  
The entry for version 0.86.0 contains the phrase “Hande idle_expires_at in silent auth.” Changing “Hande” to “Handle” will improve clarity and readability.

</blockquote></details>
<details>
<summary>packages/kysely/package.json (1)</summary><blockquote>

`14-14`: **Version Update Verification.**  
The version has been updated to "9.4.0" to reflect the changes in the adapter—specifically around idle_expires_at handling. Ensure that consumers of this package are aware of this update and that documentation (including the changelog) is consistent with this change.

</blockquote></details>
<details>
<summary>packages/kysely/migrate/migrations/2025-03-12T14:14:00_authorization_url_length.ts (2)</summary><blockquote>

`4-6`: **Revise the migration doc comment to reflect the actual changes.**

Currently, the doc references "Add a new custom domains table," which is inconsistent with this migration’s focus on `authorization_url`. Consider updating the comment accordingly.  

```diff
- * Up migration: Add a new custom domains table
+ * Up migration: Extend authorization_url length
```

---

`20-21`: **Down migration doc comment is inaccurate.**

Similarly, the comment references restoring the domains table. Please adjust to reflect the authorization_url reversion.  

```diff
- * Down migration: restore the domains table
+ * Down migration: revert authorization_url length
```

</blockquote></details>
<details>
<summary>packages/authhero/src/authentication-flows/silent.ts (2)</summary><blockquote>

`107-112`: **Reassess only updating `expires_at` without `idle_expires_at`.**

You extend `expires_at` by a fixed amount but do not update `idle_expires_at`. This may allow indefinite session extension if silent auth is repeatedly triggered. Consider aligning both timestamps or clarifying their intended difference.

---

`126-127`: **Include security attributes on the auth cookie.**

It’s generally recommended to set `Secure`, `HttpOnly`, and potentially `SameSite` attributes on session cookies to enhance security.  

```diff
- headers.set("set-cookie", cookie);
+ headers.set("set-cookie", `${cookie}; Secure; HttpOnly; SameSite=None`);
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3b2fba411d8517a0c1def636df84d50e0b96208c and b8b84d3dda6896eaf473d859fca16bde5620cfb7.

</details>

<details>
<summary>📒 Files selected for processing (9)</summary>

* `apps/demo/CHANGELOG.md` (1 hunks)
* `apps/demo/package.json` (1 hunks)
* `packages/authhero/CHANGELOG.md` (1 hunks)
* `packages/authhero/package.json` (1 hunks)
* `packages/authhero/src/authentication-flows/silent.ts` (1 hunks)
* `packages/kysely/CHANGELOG.md` (1 hunks)
* `packages/kysely/migrate/migrations/2025-03-12T14:14:00_authorization_url_length.ts` (1 hunks)
* `packages/kysely/migrate/migrations/index.ts` (2 hunks)
* `packages/kysely/package.json` (1 hunks)

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>apps/demo/CHANGELOG.md (1)</summary>

`3-10`: **Changelog Update Alignment.**  
The new version entry (0.9.28) clearly indicates that the demo app now uses authhero@0.86.0 and @authhero/kysely-adapter@9.4.0. This aligns nicely with the upstream updates. No issues noted here.

</details>
<details>
<summary>packages/kysely/migrate/migrations/index.ts (1)</summary>

`75-75`: **Looks good – alphabetical import maintained successfully!**

The newly added import and export for `n76_authorization_url_length` maintain the correct alphabetical ordering and seamlessly integrate into the existing migration exports. 



Also applies to: 153-153

</details>
<details>
<summary>apps/demo/package.json (1)</summary>

`4-4`: **Version updates and dependency bumps confirmed.**

Upgrading from `0.9.27` to `0.9.28` and aligning dependencies to newer versions (`@authhero/kysely-adapter` and `authhero`) look consistent with the recent feature changes. 



Also applies to: 9-9, 13-13

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T02:16:03Z

Review comments:
diff_hunk: @@ -0,0 +1,32 @@
+import { Kysely } from "kysely";
+import { Database } from "../../src/db";
+
+/**
+ * Up migration: Add a new custom domains table
+ */
+export async function up(db: Kysely<Database>): Promise<void> {
+  await db.schema
+    .alterTable("logins")
+    .dropColumn("authorization_url")
+    .execute();
+
+  await db.schema
+    .alterTable("logins")
+    .addColumn("authorization_url", "varchar(2048)")
+    .execute();
path: packages/kysely/migrate/migrations/2025-03-12T14:14:00_authorization_url_length.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Dropping and re-adding the column may cause data loss.**

If preserving existing data is required, consider an 'ALTER TABLE' approach. Dropping the column first discards stored values.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-13T02:16:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,32 @@
+import { Kysely } from "kysely";
+import { Database } from "../../src/db";
+
+/**
+ * Up migration: Add a new custom domains table
+ */
+export async function up(db: Kysely<Database>): Promise<void> {
+  await db.schema
+    .alterTable("logins")
+    .dropColumn("authorization_url")
+    .execute();
+
+  await db.schema
+    .alterTable("logins")
+    .addColumn("authorization_url", "varchar(2048)")
+    .execute();
+}
+
+/**
+ * Down migration: restore the domains table
+ */
+export async function down(db: Kysely<Database>): Promise<void> {
+  await db.schema
+    .alterTable("logins")
+    .dropColumn("authorization_url")
+    .execute();
+
+  await db.schema
+    .alterTable("logins")
+    .addColumn("authorization_url", "varchar(1024)")
+    .execute();
path: packages/kysely/migrate/migrations/2025-03-12T14:14:00_authorization_url_length.ts
user: login: coderabbitai[bot]
body: _🛠️ Refactor suggestion_

**Down migration also drops the column, risking data loss on rollback.**

Applying the same recommendation to the down migration helps retain data when reverting.

<!-- This is an auto-generated comment by CodeRabbit -->
created_at: 2025-03-13T02:16:02Z
author_association: CONTRIBUTOR

PR comments:
user: login: vercel[bot]
created_at: 2025-03-13T02:12:57Z
body: [vc]: #25fFCC/JiUa61SR8Qsvsyu3McsXNGX68mkJZLE+Flg0=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJhdXRoaGVyby1yZWFjdC1hZG1pbiIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiIifSwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL21hcmt1cy1haGxzdHJhbmRzLXByb2plY3RzL2F1dGhoZXJvLXJlYWN0LWFkbWluL0M1R0hzQ1dkbXNuaFJXc0NiaGlrdXNpRnhjMWMiLCJwcmV2aWV3VXJsIjoiIiwibmV4dENvbW1pdFN0YXR1cyI6IkZBSUxFRCIsInJvb3REaXJlY3RvcnkiOiJhcHBzL3JlYWN0LWFkbWluIn1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **authhero-react-admin** | ❌ Failed ([Inspect](https://vercel.com/markus-ahlstrands-projects/authhero-react-admin/C5GHsCWdmsnhRWsCbhikusiFxc1c)) |  |  | Mar 13, 2025 2:20am |


author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-03-13T02:13:00Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
This pull request updates version numbers and dependency references across multiple packages. In the demo app and both the `authhero` and `@authhero/kysely-adapter` packages, new version entries and dependency updates have been added. Additionally, the silent authentication flow in `authhero` has been refactored with improved session validation and error handling via a new helper function. A new migration increases the column length for `authorization_url` in the Kysely database, and the migration suite is updated accordingly.

## Changes

| Files | Change Summary |
|-------|----------------|
| `apps/demo/CHANGELOG.md`<br>`apps/demo/package.json` | Added new version entry "0.9.28" and updated dependencies: `authhero` upgraded to `0.86.0` and `@authhero/kysely-adapter` updated to `9.4.0`; package version bump from `0.9.27` to `0.9.28`. |
| `packages/authhero/CHANGELOG.md`<br>`packages/authhero/package.json` | Added new version entry "0.86.0" documenting minor changes (handling of `idle_expires_at` in silent auth) and updated package version from `0.85.0` to `0.86.0`. |
| `packages/authhero/src/authentication-flows/silent.ts` | Revised `silentAuth` function with enhanced session validation including `idle_expires_at`, added helper function `handleLoginRequired` for error handling, and updated logging for successful and failed authentications. |
| `packages/kysely/CHANGELOG.md`<br>`packages/kysely/package.json` | Added new version entry "9.4.0" with modifications to the idle session handling and updated package version from `9.3.0` to `9.4.0`. |
| `packages/kysely/migrate/migrations/2025-03-12T14:14:00_authorization_url_length.ts`<br>`packages/kysely/migrate/migrations/index.ts` | Introduced a new migration to update the `authorization_url` column length in the `logins` table (increased to `varchar(2048)` with a rollback to `varchar(1024)`) and added the migration to the exports index. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant Client
    participant AuthModule
    participant Logger
    participant ResponseHandler

    Client->>AuthModule: Request silent authentication
    AuthModule->>SessionValidator: Check session validity (including idle_expires_at)
    alt Session expired or user missing
        AuthModule->>Logger: Log failed silent authentication
        Logger-->>AuthModule: Logging complete
        AuthModule->>ResponseHandler: Invoke handleLoginRequired to generate error response
        ResponseHandler-->>Client: Return HTML iframe with error
    else Session valid
        AuthModule->>Logger: Log successful authentication (SUCCESS_SILENT_AUTH)
        AuthModule->>ResponseHandler: Set cookie and Server-Timing header
        ResponseHandler-->>Client: Return HTML iframe with token response
    end
```

## Possibly related PRs
- **markusahlstrand/authhero#44**: The changes in the main PR, which involve updating the `authhero` dependency to version `0.86.0`, are related to the retrieved PR that also updates the `authhero` dependency to version `0.68.0`, indicating a direct connection through the dependency versioning.
- **markusahlstrand/authhero#39**: The changes in the main PR, which involve updating the `authhero` dependency to version `0.86.0`, are related to the retrieved PR that also updates the `authhero` dependency to version `0.60.0` and includes a minor change enforcing idle expiration, indicating a shared focus on dependency management and session handling.

## Poem
> I’m a rabbit hopping with delight,  
> New versions shine so fresh and bright.  
> Dependencies grow with every leap,  
> And silent flows now sound so deep.  
> With code that dances and bugs that flee,  
> I cheer these changes with glee! 🐰✨  
> Hoppity, hoppity, onward we code!

<!-- walkthrough_end -->
<!-- This is an auto-generated comment: all tool run failures by coderabbit.ai -->

> [!WARNING]
> There were issues while running some tools. Please review the errors and either fix the tool’s configuration or disable the tool if it’s a critical failure.
> 
> <details>
> <summary>🔧 ESLint</summary>
> 
> > If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
> 
>  ERR_PNPM_OPTIONAL_DEPS_REQUIRE_PROD_DEPS  Optional dependencies cannot be installed without production dependencies
> 
> 
> 
> </details>

<!-- end of auto-generated comment: all tool run failures by coderabbit.ai -->

<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡🧪 Multi-step agentic review comment chat (experimental)</summary>
> 
> - We're introducing multi-step agentic chat in review comments. This experimental feature enhances review discussions with the CodeRabbit agentic chat by enabling advanced interactions, including the ability to create pull requests directly from comments.
> 						- To enable this feature, set early_access to true under in the settings.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between b8b84d3dda6896eaf473d859fca16bde5620cfb7 and 1779658ed9f2f7a5578bc8fecab45a3b311e89d4.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `packages/authhero/src/authentication-flows/silent.ts` (1 hunks)
* `packages/authhero/test/routes/auth-api/authorize.spec.ts` (2 hunks)

</details>

<details>
<summary>🔇 Additional comments (11)</summary><blockquote>

<details>
<summary>packages/authhero/test/routes/auth-api/authorize.spec.ts (3)</summary>

`141-141`: **Renamed variable for clarity and to match implementation**

The variable has been renamed from `expires_at` to `idle_expires_at` to reflect the intended purpose more accurately and align with the changes in the silent authentication implementation.

---

`147-147`: **Updated session creation to use idle_expires_at**

The session creation now uses `idle_expires_at` instead of `expires_at`, which is consistent with the changes in the silent authentication flow. This properly tests the idle expiration functionality.

---

`188-188`: **Test assertion updated to match expected session behavior**

The test now verifies that `idle_expires_at` is refreshed during a successful silent authentication, which matches the updated implementation in `silentAuth`. This ensures the session gets extended properly when used.

</details>
<details>
<summary>packages/authhero/src/authentication-flows/silent.ts (8)</summary>

`45-45`: **Improved URL handling**

The URL construction has been simplified by accessing protocol and host directly from the redirectURL object, making the code more readable.

---

`47-65`: **Good refactoring: Centralized error handling with helper function**

Extracting the login required logic into a helper function improves code organization and reusability. The function handles error logging and response generation consistently, which reduces duplication and improves maintainability.

---

`67-76`: **Enhanced session validation to support both expiration types**

The implementation now properly checks both `expires_at` and `idle_expires_at` to determine if a session is expired. This is a key improvement that fulfills the PR objective of handling both types of expiration times.

---

`82-84`: **Improved error handling for missing users**

The function now explicitly handles the case when a user is not found, returning a login required response with a descriptive error message. This early return simplifies the control flow and prevents null reference errors later in the code.

---

`107-120`: **Properly handling idle_expires_at in session update**

The session update logic now refreshes `idle_expires_at` based on the current time and `SILENT_AUTH_MAX_AGE_IN_SECONDS`. The conditional assignment ensures it's only updated if it already exists, maintaining backward compatibility with sessions that might not have this field.

---

`123-125`: **Updated log type for successful authentication**

The log type has been changed from `FAILED_SILENT_AUTH` to `SUCCESS_SILENT_AUTH` for successful authentication cases, which is more accurate and consistent with the outcome.

---

`129-136`: **Improved header handling for better browser compatibility**

The addition of the `Server-Timing` header prevents Cloudflare from injecting scripts that could interfere with Safari's Intelligent Tracking Prevention (ITP). Setting the cookie in headers before returning the response ensures the session is properly maintained.

---

`137-139`: **Enhanced response with proper headers**

The HTML response now includes the configured headers, ensuring that both the authentication token and the necessary HTTP headers are properly sent to the client.

</details>

</blockquote></details>

</details>
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=markusahlstrand/authhero&utm_content=57):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR