[OAUTH-WG] Re: Browser-Swapping

"Primbs, Jonas" <jonas.primbs@uni-tuebingen.de> Fri, 09 January 2026 13:56 UTC

Return-Path: <jonas.primbs@uni-tuebingen.de>
X-Original-To: oauth@mail2.ietf.org
Delivered-To: oauth@mail2.ietf.org
Received: from localhost (localhost [127.0.0.1]) by mail2.ietf.org (Postfix) with ESMTP id 35F03A55B5D3 for <oauth@mail2.ietf.org>; Fri, 9 Jan 2026 05:56:09 -0800 (PST)
X-Virus-Scanned: amavisd-new at ietf.org
X-Spam-Flag: NO
X-Spam-Score: -4.388
X-Spam-Level:
X-Spam-Status: No, score=-4.388 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_MED=-2.3, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_PASS=-0.001, T_MIME_MALF=0.01] autolearn=ham autolearn_force=no
Authentication-Results: mail2.ietf.org (amavisd-new); dkim=pass (2048-bit key) header.d=uni-tuebingen.de
Received: from mail2.ietf.org ([166.84.6.31]) by localhost (mail2.ietf.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id k6486MsPqB2X for <oauth@mail2.ietf.org>; Fri, 9 Jan 2026 05:56:06 -0800 (PST)
Received: from mx03.uni-tuebingen.de (mx03.uni-tuebingen.de [134.2.5.213]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256) (No client certificate requested) by mail2.ietf.org (Postfix) with ESMTPS id 5944FA55B50E for <oauth@ietf.org>; Fri, 9 Jan 2026 05:55:26 -0800 (PST)
Received: from exchange.uni-tuebingen.de (ex03.uni-tuebingen.de [134.2.21.163]) by mx03.uni-tuebingen.de (Postfix) with ESMTPS id 8D4CF20CE8BB; Fri, 9 Jan 2026 14:55:19 +0100 (CET)
DKIM-Filter: OpenDKIM Filter v2.11.0 mx03.uni-tuebingen.de 8D4CF20CE8BB
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=uni-tuebingen.de; s=20211202prod; t=1767966919; bh=0dkf/NPr7cw2I6OT25rH+hiUp7SxsyO/a+drshxRifc=; h=From:To:CC:Subject:Date:References:In-Reply-To:From; b=vtwfRL28R65b4dkfVu5Njb7QQWh8VU2Inryp14ZUfA3C9HAf/1LeXQ4/jxvwPC7op 7y2/EZFYT5xao7kbMGVXsM1nWUycPw/NU/Ar74XouMGsbimfW0MrYYwTkLVIimsXd7 Pms5KugqR5iUuFoPRFd7R07RdHNYhr2rt6M41DV7Nqu4YIC2zzyukrBYWOPpnqfG7J 8FwBZogPVeYSGsL5oClRxmJciBRBa+1GY37xGXxZACsvOFaTHCtuu8WYTYnwReq1R0 NGPoo++IY2v/LIpXNNOV7h6ZTxahwi00xcAGrqVjm0ZVmEXxoJLb2MsZepa13Cp3FL k6n3hT/0r7Fdg==
Received: from Ex02.uni-tuebingen.de (134.2.21.162) by EX03.uni-tuebingen.de (134.2.21.163) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.63; Fri, 9 Jan 2026 14:55:19 +0100
Received: from Ex02.uni-tuebingen.de ([fe80::5ddd:1152:31fc:6bd3]) by EX02.uni-tuebingen.de ([fe80::5ddd:1152:31fc:6bd3%7]) with mapi id 15.01.2507.063; Fri, 9 Jan 2026 14:55:19 +0100
From: "Primbs, Jonas" <jonas.primbs@uni-tuebingen.de>
To: kcloud <kraviscloud@proton.me>
Thread-Topic: [OAUTH-WG] Browser-Swapping
Thread-Index: AQHcgW+XrMDvjGHAKUO1IcWVJM6QdQ==
Date: Fri, 09 Jan 2026 13:55:19 +0000
Message-ID: <DF1EAAC0-D4BE-496B-9821-6FFAB1805025@uni-tuebingen.de>
References: <CAGBSGjoVRxszkD967my2i53VfsXLr+b1ExO4WChtGyYmMQumuA@mail.gmail.com> <16E4A2B8-416A-42F7-B76A-6F4B65138AF9@runbox.eu> <CAGBSGjpqY9WDE0L0R+jR-nmsbNO8MymBfraVfu104ywyY91e4w@mail.gmail.com> <CAMQcq-YMtu5eHHgv6+BwgLHtJq5uONX8dYTpWjXConrKN3WDRQ@mail.gmail.com> <CAJot-L3=gkpT7XBsjmzbfoTfFr68BDmyR-+wqrAJf4sXY1CSdg@mail.gmail.com> <CAMQcq-aVPGbgHFZH6CaVDSZW4pcCvdeka=go9BVGKeqchF_GnA@mail.gmail.com> <9B9C0226-CC50-4810-9A18-CEF2C5B8537A@pragmaticwebsecurity.com> <CAEayHEN9zXNU+KwE-C6VzqVgxLHxW_FT4tsQXs52aKtcOB963w@mail.gmail.com> <CAMmAzELWsW9oj9u7OzdVbOZGNEvtRpD5zq6p6X7DoUKnazLRdg@mail.gmail.com> <5731B57C-0DC7-4ED1-9783-F1B01357584A@uni-tuebingen.de> <QPG12wpyWo_t0WAgVvBDIQpk8Wv_0v8qRf17OWqraGD6YzHaCSN3RQHX8Fd0QxOF6-ts4R9yDieF5xf1J4wGmMd6KLiwciONmmhvucwS8YQ=@proton.me>
In-Reply-To: <QPG12wpyWo_t0WAgVvBDIQpk8Wv_0v8qRf17OWqraGD6YzHaCSN3RQHX8Fd0QxOF6-ts4R9yDieF5xf1J4wGmMd6KLiwciONmmhvucwS8YQ=@proton.me>
Accept-Language: de-DE, en-US
Content-Language: en-US
X-MS-Has-Attach: yes
X-MS-TNEF-Correlator:
x-mailer: Apple Mail (2.3864.300.41.1.7)
x-originating-ip: [134.2.21.181]
Content-Type: multipart/signed; boundary="Apple-Mail=_95EE51B9-075B-473B-9CBE-143EEEC2EFAE"; protocol="application/pkcs7-signature"; micalg="sha-256"
MIME-Version: 1.0
Message-ID-Hash: WDEJ5WC22GO63G4QDH7CPTLSQ3NFJU5C
X-Message-ID-Hash: WDEJ5WC22GO63G4QDH7CPTLSQ3NFJU5C
X-MailFrom: jonas.primbs@uni-tuebingen.de
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-oauth.ietf.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header
CC: "oauth@ietf.org" <oauth@ietf.org>
X-Mailman-Version: 3.3.9rc6
Precedence: list
Subject: [OAUTH-WG] Re: Browser-Swapping
List-Id: OAUTH WG <oauth.ietf.org>
Archived-At: <https://mailarchive.ietf.org/arch/msg/oauth/nWH_zwVBURLSgAo8afQ6X11S8_s>
List-Archive: <https://mailarchive.ietf.org/arch/browse/oauth>
List-Help: <mailto:oauth-request@ietf.org?subject=help>
List-Owner: <mailto:oauth-owner@ietf.org>
List-Post: <mailto:oauth@ietf.org>
List-Subscribe: <mailto:oauth-join@ietf.org>
List-Unsubscribe: <mailto:oauth-leave@ietf.org>

Hi Clint,

I get your point. This should not be the official solution to solve the Browser Swapping issue.

Moreover, I just stepped into another issue when using response_mode=form_post for desktop applications with a loopback redirect URI (which, by the way, does not need cookies at all):
Safari shows a dialog „This is a non-secure form“ when trying to send a POST request from the authorization server (HTTPS) to the loopback redirect URI (HTTP).
The reason is that Safari wants to warn users when switching from a secure (HTTPS) context to an insecure (HTTP) context, thereby ignoring the exception (step 4 [1]) that loopback addresses should be considered as secure even when using HTTP.

However, before Dick’s HTTP Redirect Header becomes usable, I think we don’t have many other solutions left.
I think we should still consider response_mode=form_post as a "temporary workaround“.

Greetings,
Jonas


[1] https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy


> Am 09.01.2026 um 10:53 schrieb kcloud <kraviscloud@proton.me>:
> 
> Hi Jonas
> 
> If my understanding is correct, the cookie seems to need the weak spot, can this process be removed for a framework that is certain.
> 
> I'm an still learning, please pardon my ignorance, if my questions seem to be off path. 
> 
> A protocol or framework that presents more options rather than for a better robust security solution in this case, should be avoided.
> 
> That's what my thoughts tell me.
> 
> Pathing a secure foundation that does not raise questions, would be the ideal approach, if possible in the environment.
> 
> Kind 
> Regards
> Clint
> 
> 
> 
> 
> Sent from Proton Mail <https://proton.me/mail/home> for Android.
> 
> 
> 
> -------- Original Message --------
> On Friday, 01/09/26 at 11:14 Primbs, Jonas <jonas.primbs@uni-tuebingen.de> wrote:
> Wouldn’t it be easier to mitigate the SameSite issue as follows?
> 
> 1. Client redirects to the authorization request endpoint by issuing a dedicated session cookie SameSite=None:
> 
> HTTP/1.1 302 Found
> Location: https://as.example.com/authorize?response_mode=form_post&state=xyz&…
> Set-Cookie: login-session=abc; SameSite=None
> 
> In the backend, the server associates the login session „abc“ with the state „xyz" and other parameters such as the code verifier or nonce.
> 
> 2. The user authorizes the client.
> 
> 3. The authorization server responds with the form that will auto-submit the following POST request to the client:
> 
> POST /callback HTTP/1.1
> Cookie: login-session=abc
> Content-Type: x-www-form-urlencoded
> 
> state=xyz&code=…
> 
> The reason SameSite=Lax or =Strict cookies are not sent with a POST request is to prevent CSRF. However, using the state parameter provides an effective CSRF protection mechanism.
> In most scenarios (login flows), the session state changes with a login anyway, so the backend can respond to this POST request by issuing a new session cookie and terminating the temporary session cookie:
> 
> HTTP/1.1 200 OK
> Set-Cookie: login-session=
> Set-Cookie: sessionid=ABC
> …
> 
> Or do I miss anything important here?
> 
> Greetings,
> Jonas
> 
> 
>> Am 05.01.2026 um 22:49 schrieb Logan Widick <logan.widick@gmail.com>:
>> 
>> It may be possible to use SameSite=Lax or SameSite=Strict cookies with the redirection endpoint. If a redirection endpoint detects  cookie loss due to SameSite, the redirection endpoint could respond with a second auto-submitting HTML form with the same data. Since the browser should be on the client's website when the second auto-submitting form is sent, the cookies should be sent also. 
>> 
>> Some possibilities for detecting cookie loss on the first auto-submitting HTML form:
>> 1. Have the two auto-submitting HTML forms use different endpoints. If the first auto-submitting HTML form's endpoint is used, assume cookie loss has occurred.
>> 2. Have the second auto-submitting form include some additional fields. If the additional fields are not set as expected, assume cookie loss has occurred.
>> 3. Analyze header values to determine if cookie loss has occurred.
>> 4. Analyze cookie values to determine if cookie loss has occurred. 
>> 
>> On Tue, Nov 25, 2025, 12:29 Thomas Broyer <t.broyer@gmail.com <mailto:t.broyer@gmail.com>> wrote:
>>> Hi all, re-upping that conversation as a client and client library developer (OIDC relying party actually)
>>> 
>>> Do we all agree that the specific scenario where authorization codes leak through near real-time access to access logs can be mitigated by always exchanging the authorization code, even when CSRF is suspected? (absence of or non-matching state; assuming a conformant AS/IdP that has single-use authorization codes)
>>> response_mode=form_post can also work but has severe implications wrt cookies (requires either using SameSite=None cookies to store "authentication state" between the redirects, or running the AS/IdP and the application "same-site"; Chromium has had a "temporary" Lax+POST mitigation that also allows this to work: https://www.chromium.org/updates/same-site/faq/#q-what-is-the-lax-post-mitigation)
>>> 
>>> And the other scenarios (brought in another thread) where the authorization code leaks through a downgrade to response_mode=fragment, or the victim sharing the URL (with authorization code) following an error, can be mitigated by always redirecting, even (particularly) in case of errors, to remove any code from the URL? (I chose to redirect to the same callback endpoint but with an error=, using a custom error value for cases like missing input –likely a response_mode=fragment downgrade– or missing session state –i.e. no cookies–, and always including a hash in the redirect URL –I use the same error=– to remove any authorization code from a response_mode=fragment downgrade)
>>> 
>>> AFAICT, hardly anything can be done if the attacker is able to block the redirect back to the client callback so it never reaches the server. IIUC, this is the only scenario where response_mode=form_post would be the only way to mitigate the attack.
>>> 
>>> And for those who deploy AS/IdP (we regularly ship Keycloak alongside our applications), they should configure them (assuming the clients are compatible) to:
>>> * enforce PKCE
>>> * forbid response_mode=fragment (and implicit grants btw)
>>> * follow other best practices (e.g. validating redirect URIs using exact URI matching)
>>> Keycloak can do that using Client Policies for instance.
>>> 
>>> Am I missing something?
>>> 
>>> Fwiw, I reproduced those scenarios (near real time access logs leak, response_mode=fragment downgrade, URL sharing) in functional tests of my client library and confirmed the above mitigations work in those cases.
>>> 
>>> Spec-wise, I'd suggest including some wording to OAuth 2.1 recommending the above-mentioned mitigations.
>>> 
>>> On Sat, Nov 8, 2025 at 8:00 AM Philippe De Ryck <philippe@pragmaticwebsecurity.com <mailto:philippe@pragmaticwebsecurity.com>> wrote:
>>>> I believe that the quoted line below is important:
>>>> 
>>>>> On 7 Nov 2025, at 10:42, Frederik Krogsdal Jacobsen <frederik.krogsdal=40criipto.com@dmarc.ietf.org <mailto:40criipto.com@dmarc.ietf.org>> wrote:
>>>>> 
>>>>> PKCE by itself does not fix this problem, but intentionally using PKCE without a verifier is one way to revoke a code without getting a token that you could accidentally use.
>>>> 
>>>> 
>>>> It seems very logical that a client implementation that needs to exchange an authorization code would need a PKCE verifier. It is trying to look it up, but due to this being a malicious or tampered with flow, that lookup is likely to fail (i.e, no PKCE verifier available). If I would write this code, I would not call the AS knowing up front that this request is going to fail. So based on this discussion, it really seems that we should make this a guideline for implementing PKCE on the client. 
>>>> 
>>>> Philippe
>>>> _______________________________________________
>>>> OAuth mailing list -- oauth@ietf.org <mailto:oauth@ietf.org>
>>>> To unsubscribe send an email to oauth-leave@ietf.org <mailto:oauth-leave@ietf.org>
>>> 
>>> 
>>> 
>>> --
>>> Thomas Broyer
>>> /tɔ.ma.bʁwa.je/ <https://ipa-reader.com/?text=t%C9%94.ma.b%CA%81wa.je&voice=Mathieu>_______________________________________________
>>> OAuth mailing list -- oauth@ietf.org <mailto:oauth@ietf.org>
>>> To unsubscribe send an email to oauth-leave@ietf.org <mailto:oauth-leave@ietf.org>
>> _______________________________________________
>> OAuth mailing list -- oauth@ietf.org
>> To unsubscribe send an email to oauth-leave@ietf.org
>