diff --git a/plugins/auth-backend/migrations/20251118120000_oauth_state_text.js b/plugins/auth-backend/migrations/20251118120000_oauth_state_text.js index f082b8b9f0..3dbef647d4 100644 --- a/plugins/auth-backend/migrations/20251118120000_oauth_state_text.js +++ b/plugins/auth-backend/migrations/20251118120000_oauth_state_text.js @@ -21,7 +21,7 @@ */ exports.up = async function up(knex) { await knex.schema.alterTable('oauth_authorization_sessions', table => { - table.text('state').nullable().alter(); + table.text('state', 'longtext').nullable().alter(); }); }; diff --git a/plugins/auth-backend/src/migrations.test.ts b/plugins/auth-backend/src/migrations.test.ts index 89def54b71..1f6a3a5dc9 100644 --- a/plugins/auth-backend/src/migrations.test.ts +++ b/plugins/auth-backend/src/migrations.test.ts @@ -324,9 +324,35 @@ describe('migrations', () => { }) .into('oidc_clients'); + // Insert a session with state before migration + const existingState = 'existing-short-state'; + await knex + .insert({ + id: 'test-existing-session', + client_id: 'test-client-id', + redirect_uri: 'https://example.com/callback', + state: existingState, + response_type: 'code', + status: 'pending', + expires_at: new Date(Date.now() + 3600000), + }) + .into('oauth_authorization_sessions'); + // Apply the migration that changes state to TEXT await migrateUpOnce(knex); + // Verify existing state persists after migration + await expect( + knex('oauth_authorization_sessions') + .where('id', 'test-existing-session') + .first(), + ).resolves.toEqual( + expect.objectContaining({ + id: 'test-existing-session', + state: existingState, + }), + ); + // Test inserting a state parameter longer than 255 characters // This is based on the real-world example from the issue const longState = 'a'.repeat(280);