|
| 1 | +# Authentication |
| 2 | +Playwright can be used to automate scenarios that require authentication. |
| 3 | + |
| 4 | +Tests written with Playwright execute in isolated clean-slate environments called |
| 5 | +[browser contexts](./core-concepts.md#browser-contexts). This isolation model |
| 6 | +improves reproducibility and prevents cascading test failures. New browser |
| 7 | +contexts can load existing authentication state. This eliminates the need to |
| 8 | +login in every context and speeds up test execution. |
| 9 | + |
| 10 | +> Note: This guide covers cookie/token-based authentication (logging in via the |
| 11 | +app UI). For [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication) |
| 12 | +use [`browser.newContext`](./network.md#http-authentication). |
| 13 | + |
| 14 | +<!-- GEN:toc --> |
| 15 | +- [Automate logging in](#automate-logging-in) |
| 16 | +- [Reuse authentication state](#reuse-authentication-state) |
| 17 | + * [Cookies](#cookies) |
| 18 | + * [Local storage](#local-storage) |
| 19 | + * [Session storage](#session-storage) |
| 20 | + * [Lifecycle](#lifecycle) |
| 21 | + * [Example](#example) |
| 22 | + * [API reference](#api-reference) |
| 23 | +- [Multi-factor authentication](#multi-factor-authentication) |
| 24 | + * [Persistent authentication](#persistent-authentication) |
| 25 | + * [Lifecycle](#lifecycle-1) |
| 26 | + * [API reference](#api-reference-1) |
| 27 | +<!-- GEN:stop --> |
| 28 | + |
| 29 | +## Automate logging in |
| 30 | + |
| 31 | +The Playwright API can automate interaction with a login form. See |
| 32 | +[Input guide](./input.md) for more details. |
| 33 | + |
| 34 | +The following example automates login on GitHub. Once these steps are executed, |
| 35 | +the browser context will be authenticated. |
| 36 | + |
| 37 | +```js |
| 38 | +const page = await context.newPage(); |
| 39 | +await page.goto('https://github.com/login'); |
| 40 | + |
| 41 | +// Interact with login form |
| 42 | +await page.click('text=Login'); |
| 43 | +await page.fill('input[name="login"]', USERNAME); |
| 44 | +await page.fill('input[name="password"]', PASSWORD); |
| 45 | +await page.click('text=Submit'); |
| 46 | +// Verify app is logged in |
| 47 | +``` |
| 48 | + |
| 49 | +These steps can be executed for every browser context. However, redoing login |
| 50 | +for every test can slow down test execution. To prevent that, we will reuse |
| 51 | +existing authentication state in new browser contexts. |
| 52 | + |
| 53 | +## Reuse authentication state |
| 54 | + |
| 55 | +Web apps use cookie-based or token-based authentication, where authenticated |
| 56 | +state is stored as [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) |
| 57 | +or in [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage). |
| 58 | +The Playwright API can be used to retrieve this state from authenticated contexts |
| 59 | +and then load it into new contexts. |
| 60 | + |
| 61 | +Cookies and local storage state can be used across different browsers. They depend |
| 62 | +on your application's authentication model: some apps might require both cookies |
| 63 | +and local storage. |
| 64 | + |
| 65 | +The following code snippets retrieve state from an authenticated page/context and |
| 66 | +load them into a new context. |
| 67 | + |
| 68 | +### Cookies |
| 69 | + |
| 70 | +```js |
| 71 | +// Get cookies and store as an env variable |
| 72 | +const cookies = await context.cookies(); |
| 73 | +process.env.COOKIES = JSON.stringify(cookies); |
| 74 | + |
| 75 | +// Set cookies in a new context |
| 76 | +const deserializedCookies = JSON.parse(process.env.COOKIES) |
| 77 | +await context.addCookies(deserializedCookies); |
| 78 | +``` |
| 79 | + |
| 80 | +### Local storage |
| 81 | +Local storage ([`window.localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)) |
| 82 | +is specific to a particular domain. |
| 83 | + |
| 84 | +```js |
| 85 | +// Get local storage and store as env variable |
| 86 | +const localStorage = await page.evaluate(() => JSON.stringify(window.localStorage)); |
| 87 | +process.env.LOCAL_STORAGE = localStorage; |
| 88 | + |
| 89 | +// Set local storage in a new context |
| 90 | +const localStorage = process.env.LOCAL_STORAGE; |
| 91 | +await context.addInitScript(storage => { |
| 92 | + if (window.location.hostname === 'example.com') { |
| 93 | + const entries = JSON.parse(storage); |
| 94 | + Object.keys(entries).forEach(key => { |
| 95 | + window.localStorage.setItem(key, entries[key]); |
| 96 | + }); |
| 97 | + } |
| 98 | +}, localStorage); |
| 99 | +``` |
| 100 | + |
| 101 | +### Session storage |
| 102 | +Session storage ([`window.sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)) |
| 103 | +is specific to a particular domain. |
| 104 | + |
| 105 | +```js |
| 106 | +// Get session storage and store as env variable |
| 107 | +const sessionStorage = await page.evaluate(() => JSON.stringify(sessionStorage)); |
| 108 | +process.env.SESSION_STORAGE = sessionStorage; |
| 109 | + |
| 110 | +// Set session storage in a new context |
| 111 | +const sessionStorage = process.env.SESSION_STORAGE; |
| 112 | +await context.addInitScript(storage => { |
| 113 | + if (window.location.hostname === 'example.com') { |
| 114 | + const entries = JSON.parse(storage); |
| 115 | + Object.keys(entries).forEach(key => { |
| 116 | + window.sessionStorage.setItem(key, entries[key]); |
| 117 | + }); |
| 118 | + } |
| 119 | +}, sessionStorage); |
| 120 | +``` |
| 121 | + |
| 122 | +### Lifecycle |
| 123 | + |
| 124 | +Logging in via the UI and then reusing authentication state can be combined to |
| 125 | +implement **login once and run multiple scenarios**. The lifecycle looks like: |
| 126 | + |
| 127 | +1. Run tests (for example, with `npm run test`). |
| 128 | +2. Login via UI and retrieve authentication state. |
| 129 | + * In Jest, this can be executed in [`globalSetup`](https://jestjs.io/docs/en/configuration#globalsetup-string). |
| 130 | +3. In each test, load authentication state in `beforeEach` or `beforeAll` step. |
| 131 | + |
| 132 | +This approach will also **work in CI environments**, since it does not rely |
| 133 | +on any external state. |
| 134 | + |
| 135 | +### Example |
| 136 | + |
| 137 | +[This example script](examples/authentication.js) logs in on GitHub.com with |
| 138 | +Chromium, and then reuses the logged in cookie state in WebKit. |
| 139 | + |
| 140 | +### API reference |
| 141 | +- [class `BrowserContext`](./api.md#class-browsercontext) |
| 142 | +- [`browserContext.cookies`](./api.md#browsercontextcookiesurls) |
| 143 | +- [`browserContext.addCookies`](./api.md#browsercontextaddcookiescookies) |
| 144 | +- [`page.evaluate`](./api.md#pageevaluatepagefunction-arg) |
| 145 | +- [`browserContext.addInitScript`](./api.md#browsercontextaddinitscriptscript-arg) |
| 146 | + |
| 147 | +## Multi-factor authentication |
| 148 | +Accounts with multi-factor authentication (MFA) cannot be fully automated, and need |
| 149 | +manual intervention. Persistent authentication can be used to partially automate |
| 150 | +MFA scenarios. |
| 151 | + |
| 152 | +### Persistent authentication |
| 153 | +Web browsers use a directory on disk to store user history, cookies, IndexedDB |
| 154 | +and other local state. This disk location is called the [User data directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md). |
| 155 | + |
| 156 | +Note that persistent authentication is not suited for CI environments since it |
| 157 | +relies on a disk location. User data directories are specific to browser types |
| 158 | +and cannot be shared across browser types. |
| 159 | + |
| 160 | +User data directories can be used with the `launchPersistentContext` API. |
| 161 | + |
| 162 | +```js |
| 163 | +const { chromium } = require('playwright'); |
| 164 | + |
| 165 | +const userDataDir = '/path/to/directory'; |
| 166 | +const context = await chromium.launchPersistentContext(userDataDir, { headless: false }); |
| 167 | +// Execute login steps manually in the browser window |
| 168 | +``` |
| 169 | + |
| 170 | +### Lifecycle |
| 171 | + |
| 172 | +1. Create a user data directory on disk |
| 173 | +2. Launch a persistent context with the user data directory and login the MFA account. |
| 174 | +3. Reuse user data directory to run automation scenarios. |
| 175 | + |
| 176 | +### API reference |
| 177 | +- [class `BrowserContext`](./api.md#class-browsercontext) |
| 178 | +- [`browserType.launchPersistentContext`](./api.md#browsertypelaunchpersistentcontextuserdatadir-options) |
0 commit comments