tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

third-party-registration.https.html (10474B)


      1 <!DOCTYPE html>
      2 <meta charset="utf-8">
      3 <script src="/resources/testharness.js"></script>
      4 <script src="/resources/testharnessreport.js"></script>
      5 <script src="/common/get-host-info.sub.js"></script>
      6 <script src="helper.js" type="module"></script>
      7 
      8 <script type="module">
      9  import {
     10    expireCookie,
     11    documentHasCookie,
     12    waitForCookie,
     13    addCookieAndSessionCleanup,
     14    setupShardedServerState,
     15    configureServer,
     16    crossSiteFetch
     17  } from "./helper.js";
     18 
     19  promise_test(async t => {
     20    await setupShardedServerState({crossSite: true});
     21    const expectedCookieAndValue = "auth_cookie=abcdef0123";
     22    const expectedCookieAttributes = `Domain=${location.hostname};Path=/device-bound-session-credentials;SameSite=Lax`;
     23    const expectedCookieAndAttributes = `${expectedCookieAndValue};${expectedCookieAttributes}`;
     24    addCookieAndSessionCleanup(t);
     25 
     26    configureServer({
     27      // Set cookie details in order to specify that we should only bind a SameSite=Lax cookie for this test.
     28      cookieDetails: [
     29        {
     30          nameAndValue: expectedCookieAndValue,
     31          attributes: expectedCookieAttributes,
     32        }
     33      ],
     34      // Since registration happens from a third-party context, we need
     35      // a SameSite=None cookie to tell us that registration completed.
     36      registrationExtraCookies: [
     37        {
     38          nameAndValue: "get_session_instructions=done",
     39          attributes: "SameSite=None;Secure",
     40        }
     41      ]
     42    });
     43 
     44    // Prompt starting a session in a third-party context, and wait until registration completes.
     45    const loginStatus = await crossSiteFetch(get_host_info().HTTPS_NOTSAMESITE_ORIGIN, `${location.protocol}//${location.host}/device-bound-session-credentials/login.py`, {credentials: "include"});
     46    assert_equals(loginStatus, 200);
     47    await waitForCookie("get_session_instructions=done", /*expectCookie=*/true);
     48 
     49    // Since all cookies in the session are first-party, registration
     50    // from a third-party context should fail.
     51    expireCookie(expectedCookieAndAttributes);
     52    assert_false(documentHasCookie(expectedCookieAndValue));
     53    const authResponseAfterExpiry = await fetch('verify_authenticated.py');
     54    assert_equals(authResponseAfterExpiry.status, 403);
     55    assert_false(documentHasCookie(expectedCookieAndValue));
     56  }, "Registration of first-party session not allowed in third-party context");
     57 
     58  promise_test(async t => {
     59    await setupShardedServerState({crossSite: true});
     60    const expectedCookieAndValueSameSiteLax = "auth_cookie_lax=abcdef0123";
     61    const expectedCookieAttributesSameSiteLax = `Domain=${location.hostname};Path=/device-bound-session-credentials;SameSite=Lax`;
     62    const expectedCookieAndAttributesSameSiteLax = `${expectedCookieAndValueSameSiteLax};${expectedCookieAttributesSameSiteLax}`;
     63 
     64    const expectedCookieAndValueSameSiteNone = "auth_cookie=abcdef0123";
     65    const expectedCookieAttributesSameSiteNone = `Domain=${location.hostname};Path=/device-bound-session-credentials;SameSite=None;Secure`;
     66    const expectedCookieAndAttributesSameSiteNone = `${expectedCookieAndValueSameSiteNone};${expectedCookieAttributesSameSiteNone}`;
     67    addCookieAndSessionCleanup(t);
     68 
     69    configureServer({
     70      // Configure the server to bind both a SameSite=Lax and SameSite=None cookie
     71      cookieDetails: [
     72        {
     73          nameAndValue: expectedCookieAndValueSameSiteLax,
     74          attributes: expectedCookieAttributesSameSiteLax,
     75        },
     76        {
     77          nameAndValue: expectedCookieAndValueSameSiteNone,
     78          attributes: expectedCookieAttributesSameSiteNone,
     79        }
     80      ],
     81      // Since registration happens from a third-party context, we need
     82      // a SameSite=None cookie to tell us that registration completed.
     83      registrationExtraCookies: [
     84        {
     85          nameAndValue: "get_session_instructions=done",
     86          attributes: "SameSite=None;Secure",
     87        }
     88      ]
     89    });
     90 
     91    // Prompt starting a session in a third-party context, and wait until registration completes.
     92    const loginStatus = await crossSiteFetch(get_host_info().HTTPS_NOTSAMESITE_ORIGIN, `${location.protocol}//${location.host}/device-bound-session-credentials/login.py`, {credentials: "include"});
     93    assert_equals(loginStatus, 200);
     94    await waitForCookie("get_session_instructions=done", /*expectCookie=*/true);
     95    // Because registration is happenin from a third-party context, only the
     96    // SameSite=None cookie will be set.
     97    assert_false(documentHasCookie(expectedCookieAndValueSameSiteLax));
     98    assert_true(documentHasCookie(expectedCookieAndValueSameSiteNone));
     99 
    100    // Since one cookies in the session is third-party, registration
    101    // from a third-party context should succeed.
    102    expireCookie(expectedCookieAndAttributesSameSiteLax);
    103    expireCookie(expectedCookieAndAttributesSameSiteNone);
    104    assert_false(documentHasCookie(expectedCookieAndValueSameSiteLax));
    105    assert_false(documentHasCookie(expectedCookieAndValueSameSiteNone));
    106    const authResponseAfterExpiry = await fetch('verify_authenticated.py');
    107    assert_equals(authResponseAfterExpiry.status, 200);
    108    // While the registration was from a third-party context, the
    109    // refresh triggered by fetching verify_authenticated.py is
    110    // happening in a first-party context. So we get both cookies from
    111    // the refresh.
    112    assert_true(documentHasCookie(expectedCookieAndValueSameSiteLax));
    113    assert_true(documentHasCookie(expectedCookieAndValueSameSiteNone));
    114  }, "Registration of session with third-party cookies allowed in third-party context");
    115 
    116  promise_test(async t => {
    117    await setupShardedServerState({crossSite: true});
    118    const expectedCookieAndValue = "auth_cookie=abcdef0123";
    119    const expectedCookieAttributes = `Domain=${location.hostname};Path=/device-bound-session-credentials;SameSite=Lax`;
    120    const expectedCookieAndAttributes = `${expectedCookieAndValue};${expectedCookieAttributes}`;
    121    addCookieAndSessionCleanup(t);
    122 
    123    configureServer({
    124      // Set cookie details in order to specify that we should only bind a SameSite=Lax cookie for this test.
    125      cookieDetails: [
    126        {
    127          nameAndValue: expectedCookieAndValue,
    128          attributes: expectedCookieAttributes,
    129        },
    130      ],
    131      earlyChallengeForNextRegisteredSession: "early_challenge",
    132    });
    133 
    134    // Prompt starting a session in a first-party context, and wait until registration completes.
    135    const loginResponse = await fetch('login.py');
    136    assert_equals(loginResponse.status, 200);
    137    await waitForCookie(expectedCookieAndValue, /*expectCookie=*/true);
    138 
    139    // Try to set the challenge from a third-party context
    140    const challengeStatus = await crossSiteFetch(get_host_info().HTTPS_NOTSAMESITE_ORIGIN, `${location.protocol}//${location.host}/device-bound-session-credentials/request_early_challenge.py`, {method: 'POST', body: JSON.stringify({ useSingleHeader: true}), credentials: "include"});
    141    assert_equals(challengeStatus, 200);
    142 
    143    // Since all cookies in the session are first-party, our attempt to
    144    // set a challenge from a third-party context should fail. This
    145    // causes a challenge mismatch at registration time.
    146    expireCookie(expectedCookieAndAttributes);
    147    assert_false(documentHasCookie(expectedCookieAndValue));
    148    const authResponseAfterExpiry = await fetch('verify_authenticated.py');
    149    assert_equals(authResponseAfterExpiry.status, 403);
    150    assert_false(documentHasCookie(expectedCookieAndValue));
    151  }, "Set challenge of first-party not allowed in third-party context");
    152 
    153  promise_test(async t => {
    154    await setupShardedServerState({crossSite: true});
    155    const expectedCookieAndValueSameSiteLax = "auth_cookie_lax=abcdef0123";
    156    const expectedCookieAttributesSameSiteLax = `Domain=${location.hostname};Path=/device-bound-session-credentials;SameSite=Lax`;
    157    const expectedCookieAndAttributesSameSiteLax = `${expectedCookieAndValueSameSiteLax};${expectedCookieAttributesSameSiteLax}`;
    158 
    159    const expectedCookieAndValueSameSiteNone = "auth_cookie=abcdef0123";
    160    const expectedCookieAttributesSameSiteNone = `Domain=${location.hostname};Path=/device-bound-session-credentials;SameSite=None;Secure`;
    161    const expectedCookieAndAttributesSameSiteNone = `${expectedCookieAndValueSameSiteNone};${expectedCookieAttributesSameSiteNone}`;
    162    addCookieAndSessionCleanup(t);
    163 
    164    configureServer({
    165      // Configure the server to bind both a SameSite=Lax and SameSite=None cookie
    166      cookieDetails: [
    167        {
    168          nameAndValue: expectedCookieAndValueSameSiteLax,
    169          attributes: expectedCookieAttributesSameSiteLax,
    170        },
    171        {
    172          nameAndValue: expectedCookieAndValueSameSiteNone,
    173          attributes: expectedCookieAttributesSameSiteNone,
    174        }
    175      ],
    176      earlyChallengeForNextRegisteredSession: "early_challenge",
    177    });
    178 
    179    // Prompt starting a session in a first-party context, and wait until registration completes.
    180    const loginResponse = await fetch('login.py');
    181    assert_equals(loginResponse.status, 200);
    182    await waitForCookie(expectedCookieAndValueSameSiteNone, /*expectCookie=*/true);
    183 
    184    // Try to set the challenge from a third-party context
    185    const challengeStatus = await crossSiteFetch(get_host_info().HTTPS_NOTSAMESITE_ORIGIN, `${location.protocol}//${location.host}/device-bound-session-credentials/request_early_challenge.py`, {method: 'POST', body: JSON.stringify({ useSingleHeader: true}), credentials: "include"});
    186    assert_equals(challengeStatus, 200);
    187 
    188    // Since one cookie in the session is third-party, our attempt to
    189    // set a challenge from a third-party context should succeed.
    190    expireCookie(expectedCookieAndAttributesSameSiteLax);
    191    expireCookie(expectedCookieAndAttributesSameSiteNone);
    192    assert_false(documentHasCookie(expectedCookieAndValueSameSiteLax));
    193    assert_false(documentHasCookie(expectedCookieAndValueSameSiteNone));
    194    const authResponseAfterExpiry = await fetch('verify_authenticated.py');
    195    assert_equals(authResponseAfterExpiry.status, 200);
    196    // While the challenge was set in a third-party context, the refresh
    197    // triggered by fetching verify_authenticated.py is happening in a
    198    // first-party context. So we get both cookies from the refresh.
    199    assert_true(documentHasCookie(expectedCookieAndValueSameSiteLax));
    200    assert_true(documentHasCookie(expectedCookieAndValueSameSiteNone));
    201  }, "Set challenge of session with third-party cookies allowed in third-party context");
    202 </script>