Skip to content

Conversation

@molly-higgins
Copy link

Description

We ran into an issue trying to unseal sessions that were generated in next.js in our api. Added an optional algorithm parameter to unseal the sessions from different environmemts

Documentation

Does this require changes to the WorkOS Docs? E.g. the API Reference or code snippets need updates.

[ ] Yes

If yes, link a related docs PR and add a docs maintainer as a reviewer. Their approval is required.

@molly-higgins molly-higgins requested a review from a team as a code owner February 4, 2026 05:31
@molly-higgins molly-higgins requested review from dandorman and removed request for a team February 4, 2026 05:31
@greptile-apps
Copy link

greptile-apps bot commented Feb 4, 2026

Greptile Overview

Greptile Summary

Added optional seal_algorithm parameter to enable cross-platform session unsealing between Ruby and Next.js environments

  • Implemented new IronSealUnseal module supporting the Iron (Fe26.2) protocol format used by iron-webcrypto/iron-session
  • Added seal_algorithm parameter to Session, AuthenticationResponse, RefreshAuthenticationResponse, and load_sealed_session with :aes_gcm default for backward compatibility
  • Iron implementation uses AES-256-CBC encryption with HMAC-SHA256 integrity verification, PBKDF2 key derivation (SHA1, 1 iteration per Iron spec), and expiration validation
  • Comprehensive test coverage for Iron seal/unseal operations including error cases and version suffix handling

Note: The Iron algorithm uses PBKDF2 with only 1 iteration and SHA1 (inherent to Fe26.2 spec), which is cryptographically weaker than modern standards but required for cross-platform compatibility.

Confidence Score: 4/5

  • Safe to merge with minor testing gaps around algorithm mismatch scenarios
  • Implementation is solid with comprehensive tests and proper backward compatibility via default parameters; critical security issue flagged (PBKDF2 weakness) is inherent to Iron spec for compatibility; main concern is missing test coverage for algorithm mismatch error handling
  • Pay attention to lib/workos/iron_seal_unseal.rb for the PBKDF2 configuration issue

Important Files Changed

Filename Overview
lib/workos/iron_seal_unseal.rb New Iron (Fe26.2) seal/unseal implementation for cross-platform session compatibility; uses PBKDF2 with 1 iteration (weak but per Iron spec)
lib/workos/session.rb Added seal_algorithm parameter throughout to support both AES-GCM and Iron algorithms; defaults to AES-GCM for backward compatibility
spec/lib/workos/session_spec.rb Added tests for Iron algorithm integration with session authentication and seal/unseal round-trip; missing algorithm mismatch tests

Sequence Diagram

sequenceDiagram
    participant Client
    participant RubyAPI as Ruby API (WorkOS)
    participant NextJS as Next.js App
    
    Note over Client,NextJS: Cross-Platform Session Unsealing
    
    NextJS->>NextJS: seal_data(session, password, algorithm: :iron)
    NextJS->>Client: Set cookie with Iron-sealed session
    
    Client->>RubyAPI: Request with Iron-sealed cookie
    RubyAPI->>RubyAPI: load_sealed_session(seal_algorithm: :iron)
    RubyAPI->>RubyAPI: Session.unseal_data(sealed, key, algorithm: :iron)
    RubyAPI->>RubyAPI: IronSealUnseal.unseal(sealed, password)
    RubyAPI->>RubyAPI: Verify HMAC, decrypt AES-256-CBC, parse JSON
    RubyAPI->>RubyAPI: authenticate() - validate access token
    RubyAPI->>Client: Authenticated response
    
    Note over Client,RubyAPI: Session Refresh Flow
    
    Client->>RubyAPI: refresh() request
    RubyAPI->>RubyAPI: Session.unseal_data(algorithm: @seal_algorithm)
    RubyAPI->>RubyAPI: authenticate_with_refresh_token(session_opts)
    RubyAPI->>RubyAPI: seal_data(new_tokens, algorithm: :iron)
    RubyAPI->>Client: New sealed session (Iron format)
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +96 to +104
def derive_key(password, salt, key_length)
OpenSSL::KDF.pbkdf2_hmac(
password,
salt: salt,
iterations: 1,
length: key_length,
hash: OpenSSL::Digest.new('SHA1'),
)
end
Copy link

Choose a reason for hiding this comment

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

PBKDF2 with only 1 iteration is cryptographically weak

Suggested change
def derive_key(password, salt, key_length)
OpenSSL::KDF.pbkdf2_hmac(
password,
salt: salt,
iterations: 1,
length: key_length,
hash: OpenSSL::Digest.new('SHA1'),
)
end
def derive_key(password, salt, key_length)
OpenSSL::KDF.pbkdf2_hmac(
password,
salt: salt,
iterations: 100000,
length: key_length,
hash: OpenSSL::Digest.new('SHA256'),
)
end
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/workos/iron_seal_unseal.rb
Line: 96:104

Comment:
PBKDF2 with only 1 iteration is cryptographically weak

```suggestion
      def derive_key(password, salt, key_length)
        OpenSSL::KDF.pbkdf2_hmac(
          password,
          salt: salt,
          iterations: 100000,
          length: key_length,
          hash: OpenSSL::Digest.new('SHA256'),
        )
      end
```

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +325 to +341
describe '.seal_data and .unseal_data' do
it 'round-trips with default algorithm (aes_gcm)' do
data = { access_token: 'tok', user: 'u1' }
sealed = WorkOS::Session.seal_data(data, cookie_password)
expect(sealed).not_to eq(data)
unsealed = WorkOS::Session.unseal_data(sealed, cookie_password)
expect(unsealed).to eq(data)
end

it 'round-trips with algorithm: :iron' do
data = { access_token: 'tok', user: 'u1' }
sealed = WorkOS::Session.seal_data(data, cookie_password, algorithm: :iron)
expect(sealed).to start_with(WorkOS::IronSealUnseal::MAC_PREFIX)
unsealed = WorkOS::Session.unseal_data(sealed, cookie_password, algorithm: :iron)
expect(unsealed).to eq(data)
end
end
Copy link

Choose a reason for hiding this comment

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

Missing test cases for algorithm mismatches

Add tests to verify error handling when unsealing data sealed with one algorithm using a different algorithm (e.g., seal with :iron, unseal with :aes_gcm)

Prompt To Fix With AI
This is a comment left during a code review.
Path: spec/lib/workos/session_spec.rb
Line: 325:341

Comment:
Missing test cases for algorithm mismatches

Add tests to verify error handling when unsealing data sealed with one algorithm using a different algorithm (e.g., seal with `:iron`, unseal with `:aes_gcm`)

How can I resolve this? If you propose a fix, please make it concise.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant