Skip to content

Add Lax + Secured cookie options#16

Open
TheMightyFid wants to merge 3 commits into
RedSoftwareSystems:mainfrom
TheMightyFid:cc/add-lax-support
Open

Add Lax + Secured cookie options#16
TheMightyFid wants to merge 3 commits into
RedSoftwareSystems:mainfrom
TheMightyFid:cc/add-lax-support

Conversation

@TheMightyFid

@TheMightyFid TheMightyFid commented Jul 6, 2026

Copy link
Copy Markdown

Add support for:

  • Unsecure cookies (firefox development/localhost).
  • SameSite::Lax from cross site authentication.

Summary by Sourcery

Add configurable session cookie security settings and improve handling of authenticated requests to the auth route.

New Features:

  • Introduce configuration flags to control the session cookie Secure attribute and to switch between Strict and Lax SameSite policies.
  • Expose builder methods to set cookie security options for different deployment and development environments.

Bug Fixes:

  • Avoid redirect loops by detecting existing valid sessions on the auth route and redirecting users directly to their target instead of restarting the OIDC flow.

Enhancements:

  • Add debug-level tracing around incoming auth requests, cookie presence, and successful token exchanges for easier authentication debugging.

Tests:

  • Extend authentication test configuration to cover the new cookie security settings.

@sourcery-ai

sourcery-ai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds configurability for session cookie security attributes (Secure flag and SameSite policy), introduces builder support for those options, updates cookie usage across auth and logout flows, and improves behavior and debug logging when an already-authenticated user hits the auth route.

Sequence diagram for auth route handling with existing session

sequenceDiagram
    actor User
    participant Router
    participant dispatch_auth
    participant AuthCache as cache
    participant App

    User->>Router: GET /auth
    Router->>dispatch_auth: dispatch_auth(configuration, cache, post_login_redirect, existing_session_id)
    alt [existing_session_id is_some]
        dispatch_auth->>cache: get_auth_session(session_id)
        alt [Ok(Some(_))]
            dispatch_auth-->>User: Redirect::to(target)
            User->>App: GET target
        else [not valid]
            dispatch_auth->>dispatch_auth: handle_auth(configuration, cache, post_login_redirect)
            dispatch_auth-->>User: auth flow response
        end
    else [no existing_session_id]
        dispatch_auth->>dispatch_auth: handle_auth(configuration, cache, post_login_redirect)
        dispatch_auth-->>User: auth flow response
    end
Loading

File-Level Changes

Change Details Files
Add secure cookie and SameSite Lax/Strict options to OAuth configuration and propagate via builder.
  • Extend OAuthConfiguration with secure_cookies and lax_same_site flags with documentation and defaults.
  • Extend OAuthConfigurationBuilder with corresponding fields, defaults, and builder methods with_secure_cookies and with_lax_same_site.
  • Include secure_cookies and lax_same_site when building the final OAuthConfiguration and update test helper configuration to set explicit defaults.
src/authentication/mod.rs
src/authentication/builder.rs
src/authentication/router/test_helpers.rs
Apply configurable cookie attributes (Secure and SameSite) to all places session cookies are set or cleared.
  • Update callback handler to set session cookie SameSite based on lax_same_site and Secure based on secure_cookies.
  • Update default logout and OIDC logout handlers to clear cookies using the configured SameSite and Secure attributes.
  • Update default auth router session cookie creation to use configurable SameSite and Secure settings.
src/authentication/router/handle_callback.rs
src/authentication/logout/handle_default_logout.rs
src/authentication/logout/handle_oidc_logout.rs
src/authentication/router/handle_default.rs
Improve auth routing behavior when a valid session already exists and add debug logging around cookie/session state.
  • Log per-request cookie header presence and decrypted session status at debug level in the main auth router.
  • Pass any existing session_id into dispatch_auth and short-circuit the OIDC flow when a live session is found in the cache, redirecting directly to the post-login target with a 303.
  • Ensure post-login redirect is validated (only absolute-path URLs, fallback to '/') to avoid redirect misuse.
src/authentication/mod.rs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • The SameSite/secure cookie configuration logic is duplicated in multiple handlers; consider extracting a small helper to construct the session cookie consistently from OAuthConfiguration.
  • In handle_callback, the session cookie max_age is hard-coded to 60 minutes while other flows use session_max_age; aligning these via configuration would avoid divergent expiration behavior.
  • The debug logging that inspects the raw COOKIE header with contains(SESSION_KEY) may produce false positives if other cookies share that substring; parsing the header into individual cookie names would be more robust.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The SameSite/secure cookie configuration logic is duplicated in multiple handlers; consider extracting a small helper to construct the session cookie consistently from `OAuthConfiguration`.
- In `handle_callback`, the session cookie `max_age` is hard-coded to 60 minutes while other flows use `session_max_age`; aligning these via configuration would avoid divergent expiration behavior.
- The debug logging that inspects the raw `COOKIE` header with `contains(SESSION_KEY)` may produce false positives if other cookies share that substring; parsing the header into individual cookie names would be more robust.

## Individual Comments

### Comment 1
<location path="src/authentication/router/handle_callback.rs" line_range="165-170" />
<code_context>
                     .http_only(true)
-                    .same_site(axum_extra::extract::cookie::SameSite::Strict)
-                    .secure(true),
+                    .same_site(if configuration.lax_same_site {
+                        axum_extra::extract::cookie::SameSite::Lax
+                    } else {
</code_context>
<issue_to_address>
**suggestion:** Cookie attribute logic (SameSite/Secure) is duplicated across multiple handlers and could benefit from a shared helper.

The SameSite/secure_cookies branching here, in the logout handlers, and in the default router should be moved into a shared helper that constructs a Cookie with the correct attributes. This will reduce duplication and avoid these code paths diverging if you later add attributes like domain or adjust SameSite handling for specific flows.

Suggested implementation:

```rust
        .await?;

    fn build_session_cookie(
        name: &'static str,
        value: String,
        lax_same_site: bool,
        secure_cookies: bool,
        max_age: Option<Duration>,
    ) -> Cookie<'static> {
        let mut builder = Cookie::build((name, value))
            .path("/")
            .http_only(true)
            .same_site(if lax_same_site {
                axum_extra::extract::cookie::SameSite::Lax
            } else {
                axum_extra::extract::cookie::SameSite::Strict
            })
            .secure(secure_cookies);

        if let Some(age) = max_age {
            builder = builder.max_age(age);
        }

        builder
    }

    tracing::debug!("auth: token exchange succeeded, session stored and cookie set");

    let jar = jar.add(build_session_cookie(
        SESSION_KEY,
        id.clone(),
        configuration.lax_same_site,
        configuration.secure_cookies,
        Some(Duration::minutes(60)),
    ));


```

To fully apply the refactoring across the codebase and remove duplication:
1. Move `build_session_cookie` (or a more generic `build_cookie_with_auth_defaults`) to a shared module (e.g. `src/authentication/cookies.rs`) and adjust its visibility (`pub` if needed).
2. Replace the SameSite/secure cookie construction in the logout handlers and in the default router with calls to this shared helper, passing the appropriate cookie name, value, and `max_age` (or `None` where not used).
3. If you move the helper to a different module, update imports/usages in `handle_callback.rs` accordingly (remove the local function and call the shared helper instead).
</issue_to_address>

### Comment 2
<location path="src/authentication/router/handle_callback.rs" line_range="171" />
<code_context>
+                axum_extra::extract::cookie::SameSite::Strict
+            })
+            .secure(configuration.secure_cookies)
             .max_age(Duration::minutes(60)),
     );

</code_context>
<issue_to_address>
**suggestion (bug_risk):** Session cookie max_age is hard-coded to 60 minutes instead of using the configured session lifetime.

In `handle_default` the session cookie duration is taken from `session_max_age`, but here it’s fixed at 60 minutes. If sessions are meant to respect the configured lifetime, consider using `session_max_age` (or the same source of truth) here as well, or clearly document why this endpoint differs.

Suggested implementation:

```rust
            .same_site(if configuration.lax_same_site {
                axum_extra::extract::cookie::SameSite::Lax
            } else {
                axum_extra::extract::cookie::SameSite::Strict
            })
            .secure(configuration.secure_cookies)
            .max_age(session_max_age),

```

1. Ensure `session_max_age` is defined in `handle_callback` in the same way it is in `handle_default` (e.g. derived from configuration, such as `configuration.session_max_age`, and converted to the appropriate `time::Duration` type).
2. If this handler sets any other session cookies with a hard-coded `Duration::minutes(60)`, update those `.max_age(Duration::minutes(60))` calls to `.max_age(session_max_age)` as well to keep cookie lifetimes consistent.
3. If `session_max_age` is currently local to `handle_default`, consider extracting a helper function or reusing the same computation logic in `handle_callback` to avoid duplication and ensure a single source of truth for session lifetime.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +165 to +170
.same_site(if configuration.lax_same_site {
axum_extra::extract::cookie::SameSite::Lax
} else {
axum_extra::extract::cookie::SameSite::Strict
})
.secure(configuration.secure_cookies)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Cookie attribute logic (SameSite/Secure) is duplicated across multiple handlers and could benefit from a shared helper.

The SameSite/secure_cookies branching here, in the logout handlers, and in the default router should be moved into a shared helper that constructs a Cookie with the correct attributes. This will reduce duplication and avoid these code paths diverging if you later add attributes like domain or adjust SameSite handling for specific flows.

Suggested implementation:

        .await?;

    fn build_session_cookie(
        name: &'static str,
        value: String,
        lax_same_site: bool,
        secure_cookies: bool,
        max_age: Option<Duration>,
    ) -> Cookie<'static> {
        let mut builder = Cookie::build((name, value))
            .path("/")
            .http_only(true)
            .same_site(if lax_same_site {
                axum_extra::extract::cookie::SameSite::Lax
            } else {
                axum_extra::extract::cookie::SameSite::Strict
            })
            .secure(secure_cookies);

        if let Some(age) = max_age {
            builder = builder.max_age(age);
        }

        builder
    }

    tracing::debug!("auth: token exchange succeeded, session stored and cookie set");

    let jar = jar.add(build_session_cookie(
        SESSION_KEY,
        id.clone(),
        configuration.lax_same_site,
        configuration.secure_cookies,
        Some(Duration::minutes(60)),
    ));

To fully apply the refactoring across the codebase and remove duplication:

  1. Move build_session_cookie (or a more generic build_cookie_with_auth_defaults) to a shared module (e.g. src/authentication/cookies.rs) and adjust its visibility (pub if needed).
  2. Replace the SameSite/secure cookie construction in the logout handlers and in the default router with calls to this shared helper, passing the appropriate cookie name, value, and max_age (or None where not used).
  3. If you move the helper to a different module, update imports/usages in handle_callback.rs accordingly (remove the local function and call the shared helper instead).

axum_extra::extract::cookie::SameSite::Strict
})
.secure(configuration.secure_cookies)
.max_age(Duration::minutes(60)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Session cookie max_age is hard-coded to 60 minutes instead of using the configured session lifetime.

In handle_default the session cookie duration is taken from session_max_age, but here it’s fixed at 60 minutes. If sessions are meant to respect the configured lifetime, consider using session_max_age (or the same source of truth) here as well, or clearly document why this endpoint differs.

Suggested implementation:

            .same_site(if configuration.lax_same_site {
                axum_extra::extract::cookie::SameSite::Lax
            } else {
                axum_extra::extract::cookie::SameSite::Strict
            })
            .secure(configuration.secure_cookies)
            .max_age(session_max_age),
  1. Ensure session_max_age is defined in handle_callback in the same way it is in handle_default (e.g. derived from configuration, such as configuration.session_max_age, and converted to the appropriate time::Duration type).
  2. If this handler sets any other session cookies with a hard-coded Duration::minutes(60), update those .max_age(Duration::minutes(60)) calls to .max_age(session_max_age) as well to keep cookie lifetimes consistent.
  3. If session_max_age is currently local to handle_default, consider extracting a helper function or reusing the same computation logic in handle_callback to avoid duplication and ensure a single source of truth for session lifetime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant