Re: Explainer: IsLoggedIn (in preparation for TPAC)

(I accidentally started a discussion with John off-thread, but putting it
back on the list for sharing :) )

On Thu, Sep 12, 2019 at 12:14 PM John Wilander <wilander@apple.com> wrote:

> On Sep 12, 2019, at 11:25 AM, Kushal Dave <kushal@scroll.com> wrote:
>
> Hi,
>
> On Sep 12, 2019, at 10:25 AM, Kushal Dave <kushal@scroll.com> wrote:
>
> Hi John,
> Really excited to see this, thanks! At first glance this seems like it
> will work quite well for us.
> A couple small things
> - I've read it a few times and still don't totally follow the motivation
> for the federated login APIs being distinct from the a site completing a
> federated login as normal and then calling setLoggedIn for itself. Is this
> tying the two logins together for logout purposes, or is this for the
> browser's ability to observe that auth happened, or something else?
>
>
> The intention is to let the browser understand that an authentication
>> happened, yes. Since there will neither be “a login flow that the browser
>> can check” such as a login form, nor WebAuthn or an invocation of a
>> password manager, there needs to be some other signal that the browser can
>> pick up to open access to the IsLoggedIn API.
>>
>
> Got it, makes sense.
>
>
>> - Reading this, I'm also curious about isLoggedIn then being a cue to
>> browser interventions. For example, it'd be great if sites trusted
>> through isLoggedIn might not generate prompts for StorageAccess so that
>> it'd be possible to deliver an even more seamless user experience, similar
>> to Firefox's implementation of the API (or maybe unlock an "always allow"
>> option?). I'm also curious if isLoggedIn could/will inform/extend some of
>> the first-party cookie deletion that happens today under ITP. Maybe out of
>> scope of this thread.
>>
>>
>> There are popular services out there that many users are logged in to and
>> that are also super prevalent trackers. We cannot open up silent storage
>> access for logged in sites without regressing tracking prevention for such
>> services.
>>
>> I don’t think Firefox has a prompt-less implementation of the Storage
>> Access API. What they have is a grace period after the prompt if the user
>> opts in, during which no user interaction is needed. At least that’s how I
>> understood it.
>>
>> A more likely development on our side is that IsLoggedIn will become a
>> requirement for calling the Storage Access API at all.
>>
>
>> It should be noted that isLoggedIn() will probably have to be restricted
>> in some fashion to not allow a multitude of popular services to collude as
>> third-parties and create a fingerprint of the user. Imagine 32 popular
>> services all calling isLoggedIn() in hidden iframes and reporting to the
>> top page or to each other whether the user is logged in or not.
>>
>
> Interesting. I see your point — since isLoggedIn in this model really is
> more about what's happening behind the scenes than the user expressing
> additional trust beyond having literally logged in to something, it's more
> like a bare minimum for StorageAccess. I do still wonder if there is some
> way to streamline StorageAccess for good actors while also mitigating
> isLoggedIn abuse by bad ones through user consent or notification.
>
>
> You’d be amazed at what companies often believed or referred to as “good
> actors” in this space are up to. This is about financial incentives.
> Bonuses and stock options are on the line. Developers are pressured to do
> immoral changes – silent, sneaky changes. I don’t see a way to trust
> anyone. Our tracking preventions doesn’t trust Apple in any special way
> either.
>

Totally. I think similar to Brian's point, though, in the app ecosystem
there is a sense that when a user downloads an app and grants it
permissions, they are expressing trust in the app developer, and the
platform defers to that trust. Sure, there's some amount of policing, but
the app has a decent amount of latitude to do shifty things and users can
and do consider whether they trust a given app developer before downloading
it. If the platform were built around the worst case/lowest denominator
privacy scenario, apps with contacts access might never be allowed to make
network requests for fear of what might happen to the data from there, for
example.

In the web ecosystem, although many things are ephemeral, there are also
some longed-lived relationships. But there's no equivalent way for users to
say "I trust this site, and although I want warnings if they do anything
that looks really bad, I mostly want my experience of this site to be
seamless." isLoggedIn feels like it could be a step in this direction, but
in some ways the use of implicit signals means it loses some of its value.
Although even with explicit permission, there's a risk that a user
expresses trust in an actor that the platform thinks they shouldn't, and
then it's more of a philosophical question whether just like an expired SSL
cert, the user should still have some ability to override, or if it's a
hard wall.

In the specific case of StorageAccess, the user already has to actively
engage with an iframe in order to trigger the request, and that seems like
the sort of thing where a user could easily say "Yes, if I click on an
embedded video, I'm okay with it going into my video.com history, now and
for the next 30 days" especially when layered on top of an isLoggedIn
signal. Could this then be abused by an ad network? Maybe, but at least
some of the StorageAccess calls from ads would trigger user-visible
prompts, so it'd be hard to fly under the radar.

Anyway, this is all pretty far afield of isLoggedIn, which I still think is
a step in the right direction of trying to understand which sites a user
has a real relationship with and feeding that carefully into smarter
cross-site behavior and storage management. Thanks for engaging on this,
and good luck next week!



>
> As far as Firefox, I was thinking of this, although I see that it's a
> temporary thing now:
> "In order to improve web compatibility, Firefox currently includes some
> heuristics to grant storage access automatically to third parties that
> receive user interaction. These heuristics are intended to allow some
> third-party integrations that are common on the web to continue to
> function. They are intended to be temporary and will be removed in a future
> version of Firefox. They should not be relied upon for current and future
> web development.”
>
>
> I believe this refers to Mozilla’s replication of our compatibility fix
> for federated login popups. See the ITP 2.0 blogpost.
>
>
>    Regards, John
>
> On Wed, Sep 11, 2019 at 3:11 PM John Wilander <wilander@apple.com> wrote:
>>
>>> Hi WebAppSec!
>>> TPAC is around the corner and one of the agenda items is "Login API”
>>> with my name attached to it. Below is an explainer of what we want to
>>> achieve. I think Wendy is working on an “Unofficial Drafts” repo for our
>>> working group where I can put this for more of issue tracking style
>>> feedback. But I wanted to get something out today since there’s not much
>>> time left before we meet in Fukuoka.
>>>
>>> Looking forward to the event and your thoughts on IsLoggedIn.
>>>
>>>    Regards, John
>>>
>>> # Explainer: IsLoggedIn## MotivationWe need a way for websites to
>>> declare to the browser that the user is logged in or out, and for those
>>> declarations to be trustworthy. This is why:
>>> ### The Browser Should Know Where You’re Logged InIn olden times,
>>> Basic/Digest Authentication offered a way for browsers to know where the
>>> user was logged in and help them to stay logged in. Those technologies are
>>> obsolete for many reasons. Today, WebAuthn
>>> <https://w3c.github.io/webauthn/> and password managers (including the
>>> use of Credential Management
>>> <https://w3c.github.io/webappsec-credential-management/>) offer a
>>> browser-managed way to log in but those features neither cover the expiry
>>> of the logged in session nor the act of logging out. We have yet to
>>> standardize a way for browsers to manage the logged in status.
>>>
>>> ### The Current (Bad) Behavior Is “Logged In By Default”For the
>>> purposes of client-side storage/state, the behavior of the web platform has
>>> been “logged in by default,” meaning as soon as the browser loads a
>>> webpage, that page can store data virtually forever on the device, and the
>>> browser may have to treat the user as logged in to that website. That is a
>>> serious privacy issue. Long term storage should instead be tied to where
>>> the user is truly logged in.
>>>
>>> DOM storage such as IndexedDB doesn’t even have an expiry functionality,
>>> making it impossible for websites who use these storage mechanisms to state
>>> any guarantees on when traces of a visit to their site will go away.
>>>
>>> As an additional note, allowing an ever growing, never expiring pile of
>>> website data from sites the user may have visited just once is bad for disk
>>> space and backup space.
>>>
>>> ### Clearing Website Data May Log Users OutBrowsers may try to fix or
>>> mitigate the privacy implications of “logged in by default” by cleaning up
>>> storage and state at some cadence. Browsers may also evict cookies because
>>> of storage limits or truncate cookie request headers because of header
>>> limits. If the browser doesn’t know where the user is logged in, website
>>> data cleaning or limits may inadvertently log the user out of some
>>> websites, leading to a bad user experience.
>>>
>>> ## Straw Man ProposalBelow we present a straw man proposal for how a
>>> web API for logged in status could look and work. This is a starting point
>>> for a conversation, not a fully baked proposal.
>>> ### APIHere’s how the API for setting IsLoggedIn to true could look:
>>>
>>> navigator.setLoggedIn(
>>>     username: non-whitespace string of limited length,
>>>     credentialTokenType: “httpStateToken” OR “legacyAuthCookie”,
>>>     optionalParams { }
>>> ) –> Promise<void>
>>>
>>> The returned promise would resolve if the status was set and reject if
>>> not. The API could potentially take an expiry parameter but here we’re
>>> assuming that a designated HTTP State Token
>>> <https://mikewest.github.io/http-state-tokens/draft-west-http-state-tokens.html> or
>>> “legacy auth cookie” manages the expiry of the login through their own
>>> mechanisms.
>>>
>>> Here’s how the API for setting IsLoggedIn to false could look:
>>>
>>> navigator.setLoggedOut(optionalUsername) –> Promise<void>
>>>
>>> The optional username parameter highlights that we might want to support
>>> concurrent logins on the same website which would require the site to keep
>>> track of who to log out and credential tokens to be scoped to user names.
>>>
>>> Here’s how the API for checking the IsLoggedIn status could look:
>>>
>>> navigator.isLoggedIn() –> Promise<bool>
>>>
>>> This last API could potentially be allowed to be called by third-party
>>> iframes that do not currently have access to their cookies and website
>>> data. The iframes may want to render differently depending on whether the
>>> user is one of their logged in customers or not.
>>>
>>> ### Defending Against AbuseIf websites were allowed to set the
>>> IsLoggedIn status whenever they want, it would not constitute a trustworthy
>>> signal and would most likely be abused for user tracking. We must therefore
>>> make sure that IsLoggedIn can only be set when the browser is convinced
>>> that the user meant to log in or the user is already logged in and wants to
>>> stay logged in.
>>>
>>> Another potential for abuse is if websites don’t call the logout API
>>> when they should. This could allow them to maintain the privileges tied to
>>> logged in status even after the user logged out.
>>>
>>> There are several ways the browser could make sure the IsLoggedIn status
>>> is trustworthy:
>>>
>>>    - Require websites to use of WebAuthn or a password manager
>>>    (including Credential Management) before calling the API.
>>>    - Require websites to take the user through a login flow according
>>>    to rules that the browser can check. This would be the escape hatch for
>>>    websites who can’t or don’t want to use WebAuthn or a password manager but
>>>    still want to set the IsLoggedIn bit.
>>>    - Show browser UI acquiring user intent when IsLoggedIn is set.
>>>    Example: A prompt.
>>>    - Continuously show browser UI indicating an active logged in
>>>    session on the particular website. Example: Some kind of indicator in the
>>>    URL bar.
>>>    - Delayed browser UI acquiring user intent to stay logged in, shown
>>>    some time after the IsLoggedIn status was set. Example: Seven days after
>>>    IsLoggedIn was set – “Do you want to stay logged in to news.example?”
>>>    - Requiring engagement to maintain logged in status. Example:
>>>    Require user interaction as first party website at least every N days to
>>>    stay logged in. The browser can hide instead of delete the credential token
>>>    past this kind of expiry to allow for quick resurrection of the logged in
>>>    session.
>>>
>>>
>>> ### Credential TokensIdeally, a new IsLoggedIn API like this would only
>>> work with modern login credentials. HTTP State Tokens could be such a
>>> modern piece. However, to ensure a smooth path for adoption, we probably
>>> want to support cookies as a legacy option.
>>>
>>> Both HTTP State Tokens and cookies would have to be explicitly set up
>>> for authentication purposes to work with IsLoggedIn. In the case of both of
>>> these token types, we could introduce a __auth- prefix as a signal that
>>> both the server and client consider the user to be logged in. Or we could
>>> allow HTTP State Token request and response headers to convey login status.
>>> Note that sending metadata in *requests* differs from how cookies work.
>>>
>>> The expiry of the token should be picked up as a logout by IsLoggedIn.
>>>
>>> Cookies have the capability to span a full registrable domain and thus
>>> log the user in to all subdomains at once. HTTP State Tokens have a proper
>>> connection to origins but can be declared to span the full registrable
>>> domain too. We should probably let the credential token control the scope
>>> of the IsLoggedIn status.
>>>
>>> Explicitly logging out should clear all website data for the website,
>>> not just the credential token. The reverse, the user clearing the
>>> credential token (individually or as part of a larger clearing of website
>>> data), should also log them out for the purposes of IsLoggedIn.
>>>
>>> ### Federated LoginsSome websites allow the user to use an existing
>>> account with a federated login provider to bootstrap a new local user
>>> account and subsequently log in. The IsLoggedIn API needs to support such
>>> logins.
>>>
>>> First, the federated login provider needs to call the API on its side,
>>> possibly after the user has clicked a “Log in with X” button:
>>>
>>> navigator.initiateLoggedInFederated(destination: secure origin) –>
>>> Promise<void>
>>>
>>> For the promise to resolve, the user needs to already have the
>>> IsLoggedIn status set for the federated login provider, i.e. the user needs
>>> to be logged in to the provider first.
>>>
>>> Then the destination website has to call the API on its side:
>>>
>>> navigator.setLoggedInFederated(
>>>     loginProvider: secure origin,
>>>     username,
>>>     credentialTokenType,
>>>     optionalParams { }
>>> ) –> Promise<void>
>>>
>>> The promise would only resolve if the loginProvider had recently called
>>> setLoggedInFederated() for this destination website.
>>>
>>> ## Challenges and Open Questions
>>>
>>>    - *Grandfathering*. Some websites may not want to prompt an already
>>>    logged in user or take them through an additional login flow just to set
>>>    the IsLoggedIn status.
>>>    - *Expiry limit*. What is a reasonable limit for expiry without
>>>    revisit/re-engagement?
>>>    - *Single sign-on*. If the browser supports First Party Sets
>>>    <https://github.com/krgovind/first-party-sets>, it may support
>>>    single sign-on within the first party set, for instance with an optional
>>>    parameter includeFirstPartySet: [secure origin 1, secure origin 2].
>>>    The browser would check the integrity of the first party set claim and
>>>    potentially ask the user for their intent to log in to multiple websites at
>>>    once before setting the IsLoggedIn status for all of them. The expiry of
>>>    the login status for the first party set would likely be controlled by the
>>>    expiry of the credential token for the single sign-on origin. However,
>>>    there is not browser agreement on how to support First Party Sets in a
>>>    privacy preserving way (see Issue 6
>>>    <https://github.com/krgovind/first-party-sets/issues/6> and Issue 7
>>>    <https://github.com/krgovind/first-party-sets/issues/7>).
>>>    - *A full-fledged **Login API*. As we’ve discussed IsLoggedIn, we’ve
>>>    also talked about what an API that actually logs the user in would look
>>>    like, i.e. navigator.logIn(), navigator.logOut(), and
>>>    navigator.isLoggedIn(), where either credential tokens are passed to
>>>    the browser or the browser is asked to generate them. That may be where the
>>>    WG prefers to go so we’re adding it here.
>>>
>>>
>>>
>>
>

Received on Sunday, 15 September 2019 12:28:38 UTC