go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/encryptedcookies/doc.go (about) 1 // Copyright 2021 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package encryptedcookies implements authentication using encrypted cookies. 16 // 17 // A session cookie contains a session ID and a per-session encryption key. 18 // The session cookie is itself encrypted (using an AEAD scheme) with the 19 // server-global primary encryption key. The session ID points to a session 20 // entity in a database that contains information about the user as well as 21 // various OAuth2 tokens established during the login flow when the session 22 // was created. Tokens are encrypted (again using an AEAD scheme) with the 23 // per-session encryption key from the cookie. 24 // 25 // One of the stored tokens is an OAuth2 refresh token. Periodically, during the 26 // session validation process, it is used to refresh other stored tokens 27 // (in particular an OAuth2 access token and an OpenID Connect ID token). This 28 // procedure fails if the user revoked access to their accounts via the ID 29 // provider or if the user's account is not longer active (from the ID 30 // provider's point of view). That way the state of the session is tied to the 31 // state of the user account at the ID provider which fully offloads user 32 // accounts management to the ID provider. 33 // 34 // # Configuration 35 // 36 // To use this module you'll need to create an encryption key and to register 37 // an OAuth2 client with the ID provider. Instructions below assume you are 38 // using the Google Accounts ID provider and the created encryption key will be 39 // used as the server primary encryption key (i.e. it will be used to encrypt 40 // not only cookies but also any other secrets that the server may wish to 41 // encrypt). 42 // 43 // Start by creating two Google Secret Manager secrets (with no values) named 44 // `tink-aead-primary` and `oauth-client-secret` in the cloud project that 45 // the server is running in (referred to as `<cloud-project>` below). Make sure 46 // the server account has "Secret Manager Secret Accessor" role in them. These 47 // steps are usually done using Terraform. 48 // 49 // Initialize `tink-aead-primary` key by using 50 // https://go.chromium.org/luci/server/cmd/secret-tool tool: 51 // 52 // cd server/cmd/secret-tool 53 // go run main.go create sm://<cloud-project>/tink-aead-primary -secret-type tink-aes256-gcm 54 // 55 // This secret now contains a serialized Tink keyset with the primary encryption 56 // key. If necessary it can be rotated using the same `secret-tool` tool: 57 // 58 // cd server/cmd/secret-tool 59 // go run main.go rotation-begin sm://<cloud-project>/tink-aead-primary 60 // # wait several hours for the new key to propagate into all caches 61 // # confirm by looking at /chrome/infra/secrets/gsm/version metric 62 // go run main.go rotation-end sm://<cloud-project>/tink-aead-primary 63 // 64 // This will add a new active key to the keyset. It will be used to encrypt 65 // new cookies, but the old key will still be recognized when decrypting 66 // existing cookies. 67 // 68 // Next, create a new OAuth2 client ID that will represent your server. Follow 69 // instructions on https://support.google.com/cloud/answer/6158849?hl=en and 70 // pick the application type "Web application". Add an authorized redirect URI 71 // equal to "https://<your-server-host>/auth/openid/callback". Do not add any 72 // authorized JavaScript origins. 73 // 74 // After creating the OAuth2 client, note the client ID (usually looks like 75 // "<number>-<gibberish>.apps.googleusercontent.com") and the client secret 76 // (just a random looking string). Put the value of the secret into a new 77 // `oauth-client-secret` Google Secret Manager secret using the `secret-tool`: 78 // 79 // cd server/cmd/secret-tool 80 // go run main.go create sm://<cloud-project>/oauth-client-secret -secret-type password 81 // # paste the client secret 82 // 83 // All prerequisites are done. Pass the following flags to the server binary to 84 // instruct it to use the generated secrets and the OAuth2 client: 85 // 86 // server \ 87 // ... 88 // -primary-tink-aead-key sm://tink-aead-primary \ 89 // -encrypted-cookies-client-id <number>-<gibberish>.apps.googleusercontent.com \ 90 // -encrypted-cookies-client-secret sm://oauth-client-secret \ 91 // -encrypted-cookies-redirect-url https://<your-server-host>/auth/openid/callback 92 // 93 // Note that the value of `-encrypted-cookies-redirect-url` must match exactly 94 // what you specified when creating the OAuth2 client (e.g. if you used some 95 // custom DNS domain name there, specify it in the `-encrypted-cookies-redirect-url` 96 // as well). 97 // 98 // If you want to use a dedicated key set for encrypting cookies specifically, 99 // replace `-primary-tink-aead-key` with `-encrypted-cookies-tink-aead-key` 100 // (and perhaps use some different name for the secret). 101 // 102 // # Session store 103 // 104 // The module needs to know where and how to store user sessions. Link to 105 // a concrete implementation (e.g. Cloud Datastore) by using the following 106 // blank import line in the main.go: 107 // 108 // import ( 109 // ... 110 // // Store auth sessions in the datastore. 111 // _ "go.chromium.org/luci/server/encryptedcookies/session/datastore" 112 // ) 113 // 114 // # Inactive sessions cleanup 115 // 116 // When using Cloud Datastore as a session storage, configure a time-to-live 117 // policy to delete `encryptedcookies.Session` entities based on `ExpireAt` 118 // field. See https://cloud.google.com/datastore/docs/ttl. This step is usually 119 // done via Terraform. 120 // 121 // A session is considered expired if it wasn't accessed for more than 14 days. 122 // 123 // # Exposed routes 124 // 125 // The module exposes 3 routes involved in the login/logout process: 126 // `/auth/openid/login`, `/auth/openid/logout` and `/auth/openid/callback`. When 127 // configuring your load balancer (or dispatch.yaml on Appengine), make sure 128 // `/auth/openid/*` requests are routed appropriately. 129 // 130 // Note that cookies established by one server process can be validated by 131 // another, as long as they are both configured identically (i.e. all CLI flags 132 // mentioned above are passed to both binaries). For example, you can configure 133 // the load balancer to pass all `/auth/openid/*` requests to a dedicated server 134 // responsible for the login/logout, but then validate user cookies on another 135 // server. 136 // 137 // Note also that the server that only validates cookies still needs write 138 // access to the session store, to be able to refresh encrypted tokens stored 139 // there. It means if there are some caching layers in front of the datastore, 140 // they must be configured identically across all servers as well. 141 // 142 // # Running locally 143 // 144 // If the server is starting in the development mode (no `-prod` flag is 145 // passed), and the `-encrypted-cookies-client-id` flag is not set, the module 146 // switches to use fake cookies that have a similar semantics to the real 147 // encrypted cookies, but require no extra configuration. They are absolutely 148 // insecure and must never be used outside of local runs. They exist only to 149 // simplify the local development of servers that use LoginURL/LogoutURL APIs. 150 package encryptedcookies